Agenda

Announcements

  • Keep up with Piazza
  • Canvas assignments
    • MDSR Chap 5 Exercises
    • Mini-Project: Bike Share and Local Weather
    • Programming Notebooks: MDSR Chapter 07
      • Your results may differ from the book in Sec 7.2 & Sec 7.3 (it’s OK)
      • likely set.seed() issue, so random/simulated processes differ

Statistical Foundations

Image credit: R for Data Science (https://r4ds.had.co.nz/model-intro.html)

Image credit: R for Data Science (https://r4ds.had.co.nz/model-intro.html)

Key ideas: Samples, populations, sampling distributions

Statistical Foundations

Example: meet Chris

rr require(tidyverse) require(mdsr) require(mosaic) require(nycflights13) data(flights) # Get flights from NYC to ORD OrdDelays <- flights %>% filter(dest == )

Example: flight delays from New York to Chicago

rr OrdDelays28 <- OrdDelays %>% sample_n(size = 28)

rr # summary statistics of delays favstats( ~ arr_delay, data = OrdDelays28)

Example: flight delays from New York to Chicago

rr tolerance28 <- qdata(~ arr_delay, p = 0.97, data = OrdDelays28) tolerance28

       p quantile 
    0.97   103.16 

Example: flight delays from New York to Chicago

rr # reality of population (unknown/unknowable to Chris) favstats( ~ arr_delay, data = OrdDelays) # full data r # population value for 0.97 quantile tolerance <- qdata(~ arr_delay, p = 0.97, data = OrdDelays) # full data tolerance

       p quantile 
    0.97   132.00 

rr # Actual performance for Chris… tally(~ arr_delay < tolerance28[2], data = OrdDelays, format = )

arr_delay < tolerance28[2]
      TRUE      FALSE       <NA> 
0.91511890 0.04339524 0.04148585 

Problem solved? (Not quite)

rr # Results vary… four different samples = four different results n <- 28 qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))

       p quantile 
    0.97    83.54 

rr qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))

       p quantile 
    0.97    45.28 

rr qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))

       p quantile 
    0.97    88.86 

rr qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE))

       p quantile 
    0.97    59.12 

Distribution of the estimate depends on sample size

rr n <- 28 SimsSmallN <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = n, replace = TRUE)) # inspect result head(SimsSmallN) r # variability of our 0.97 quantile estimate (note ) favstats(~ quantile, data = SimsSmallN)

Vocabulary

  • Sample size (n) is the number of cases in an observed sample
  • Sampling distribution is the collection of the sample statistic from all trials
    • a theoretical sampling distribution includes all possible trials
    • we have 1000 simulated trials,
      • that specific number doesn’t matter much as long as it’s “large”
      • 10s of thousands or millions are common in practice
      • do more if a precise result is important
  • shape of the sampling distribution is worth noting (ours is right-skewed here)
  • standard error is the standard deviation of the sampling distribution for a statistic
    • Here, we estimate the SE using the SD of many simulated 0.97 quantile estimates
    • you can find this result in our favstats() output above

rr SimsSmallN %>% ggplot() + geom_histogram(aes(x = quantile)) + ggtitle(\1000 simulated 0.97 quantiles for flight delays among samples of n = 28)

What if we have a bigger sample?

rr nBigger <- 150 SimsBigN <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdDelays, size = nBigger, replace = TRUE)) # inspect result head(SimsSmallN)

Plot comparison (n = 28; n = 150)

rr # combine results into a single tbl BothSims <- bind_rows(SimsSmallN %>% mutate(SampleSize = n), SimsBigN %>% mutate(SampleSize = nBigger)) # summary comparison favstats(quantile ~ SampleSize, data = BothSims) r # plot comparison BothSims %>% ggplot(aes(x = quantile)) + geom_histogram(bins = 30) + facet_grid( ~ SampleSize) + xlab(0.98 Quantile)

Access to Sampling Distribution

Bootstrapping

Discussion Questions:

  • Why do we need to sample with replacement?
  • Does bootstrapping collect new data?
  • Do you expect the mean of the bootstrap sampling distribution to be equivalent to the mean of the (true) sampling distribution?
  • Do you expect the standard error of the bootstrap sampling distribution to be equivalent to the standard error of the (true) sampling distribution?

Back to Chris…

Chris wants to be more confident that flight delays will not make him late more than 3% of the time, what should he do?
- He pools flight delay data with colleagues and they come up with a much bigger data set of 230 flights - We’ll call it OrdColleagueData

Bootstrap confidence interval

  1. using the data combined among Chris’ colleagues–OrdColleagueData–we sample with replacement and calculate the 0.97 quantile each time.
  2. we now have a distribution of bootstrap sample quantile estimates (1000 of them in this case)
  3. we use that distribution to produce an interval estimate
    • In this case, a confidence interval for 97th percentile of arrival delay

bootstrap sampling distribution

rr # Chris and colleagues pool data for 230 samples OrdColleagueData <- OrdDelays %>% sample_n(size = 230, replace = FALSE) # bootstrap distribution BootStrapTrials <- mosaic::do(1000) * qdata(~ arr_delay, p = 0.97, data = sample_n(OrdColleagueData, size = 230, replace = TRUE))

confidence interval

  • If the bootstrap sampling distribution looks approximately Normal:
    • compute the Chris’s sample mean
    • estimate the standard error using the standard deviation of the bootstrap sampling distribution
    • Confidence interval: \(est \pm z^* (SE)\)
      • “est” is the estimate from the sample (Chris’ data)
      • \(z^*\) is a critical value from the Normal distribution commensurate with confidence level (e.g. 1.96 for 95% CI)
      • \(SE\) is the standard error which we’ve estimated from the standard deviation of the bootstrap distribution
  • If bootstrap sampling distribution is fairly smooth, but not symmetric (e.g. skewed),
    • we could use a percentile method to approximate our confidence interval
    • not perfect, but still useful
  • If bootstrap sampling distirbution has big gaps or other problems,
    • we should think hard about alternative solutions…
    • e.g., what’s making the sampling distribution so ugly?

Our results?

  • the distribution is not pretty… it’s sort of a borderline case
    • definitely not Normal, but possibly still useful
    • we should even be suspicious of a bootsrap percentile interval
      • we’ll suppose he wants to 80% confident his flights will arrive on time 97% of the time
  • Discussion questions
    • Q: How should Chris decide how early he should plan to arrive?
    • Q: Why could make this bootstrap sampling distribution look so strange?

rr BootStrapTrials %>% ggplot() + geom_histogram(aes(x = quantile))

rr # Maybe Chris just wants to be 80% confident he will be on time 97% of the time qdata(~ quantile, p = 0.8, data = BootStrapTrials)

       p quantile 
     0.8    105.2 

Outliers

Long delays

rr OrdDelays %>% filter(arr_delay >= 360) %>% transmute(carrier, month, day, dep_delay, arr_delay, air_delay = arr_delay - dep_delay) %>% arrange(desc(arr_delay))

132 minute delays

  • Actually, American Eagle (MQ) isn’t so bad after looking at carrier volume & comparing with Chris’ threshold
  • Best carrier performance
    • American Airlines looks better than average
    • United meets expectation;
  • Below average carrier performance
    • Pinnacle Airlines (9E)
    • JetBlue (B6)

rr # inspect long delay volume OrdDelays %>% filter(!is.na(arr_delay)) %>% mutate(long_delay = arr_delay >= 132) %>% tally( ~ long_delay | carrier, data = ., format = , margins = TRUE)

          carrier
long_delay   9E   AA   B6   EV   MQ   OO   UA
     TRUE    56  130   44    0   74    0  194
     FALSE  928 5716  848    2 2023    1 6550
     Total  984 5846  892    2 2097    1 6744

rr # inspect long delay proportion OrdDelays %>% filter(!is.na(arr_delay)) %>% mutate(long_delay = arr_delay >= 132) %>% tally( ~ long_delay | carrier, data = ., format = ) %>% round(2)

          carrier
long_delay     9E     AA     B6     EV     MQ     OO     UA
     TRUE    5.69   2.22   4.93   0.00   3.53   0.00   2.88
     FALSE  94.31  97.78  95.07 100.00  96.47 100.00  97.12

We need a way to account for other variables in our model…

rr # for simplicity, we model average arrival delays here ordModel <- OrdColleagueData %>% mutate(carrierAA = if_else(carrier %in% c(), true = , false = ), season = if_else(month %in% 3:5, true = , false = )) %>% lm(arr_delay ~ carrierAA + season, data = .) # How to interpret? What have we learned? msummary(ordModel)

                      Estimate Std. Error t value Pr(>|t|)
(Intercept)             0.1045     4.5112   0.023    0.982
carrierAAOtherCarrier   3.9967     5.2874   0.756    0.451
seasonSpring           -1.3640     6.1857  -0.221    0.826

Residual standard error: 35.98 on 215 degrees of freedom
  (12 observations deleted due to missingness)
Multiple R-squared:  0.002808,  Adjusted R-squared:  -0.006469 
F-statistic: 0.3027 on 2 and 215 DF,  p-value: 0.7392

rr confint(ordModel)

                           2.5 %    97.5 %
(Intercept)            -8.787304  8.996376
carrierAAOtherCarrier  -6.425081 14.418442
seasonSpring          -13.556301 10.828320

Segue to Model Basics

Multiple Testing

rr # 3 Tests… 1 - (1 - 0.05)^3

[1] 0.142625

rr # Bonferroni adjustment for 3 tests b <- 0.05 / 3 1 - (1 - b)^3

[1] 0.0491713

ASA Statement on p-values…

Hypothesis generation vs. hypothesis confirmation

Model Basics

Goals of modeling:

  • “provide a simple, low-dimensional summary of a data set” (R4DS)
  • You may use these tools in different contexts for various purposes
    • description
    • inference
    • prediction
  • In each case, we are explaining variation
    • some variation in the data is structural
    • some variation in the data is simply randomness

Choosing a model

All models are wrong (some are useful)

Moral: all models are wrong; some are useful

  1. Newtonian physics was known to be wrong for nearly 2 centuries before relativity came along, in part because it failed to accurately predict the orbit of Mercury. The model was (and is?) useful for tons of practical purposes

  2. Einstein’s theory of relativity is almost certainly wrong too because it fails for subatomic particles… but it’s still useful

Simple example

rr sim1 %>% ggplot(aes(x = x, y = y)) + geom_point()

Simple example (random models)

rr models <- tibble( b0 = runif(250, -20, 40), b1 = runif(250, -5, 5) ) sim1 %>% ggplot(aes(x, y)) + geom_abline(aes(intercept = b0, slope = b1), data = models, alpha = 0.25) + geom_point()

Distance from points to the model (1/4)

Distance from points to the model (2/4)

rr model1 <- function(a, data) { # purpose: calculate predictions for a given model # inputs: ### a: vector of coefficient estimates for linear model (e.g. intercept & slopes) ### data: vector of observed response data

a[1] + data$x * a[2] } # show model predictions model1(c(7, 1.5), sim1)

 [1]  8.5  8.5  8.5 10.0 10.0 10.0 11.5 11.5 11.5 13.0 13.0 13.0 14.5 14.5 14.5 16.0 16.0 16.0
[19] 17.5 17.5 17.5 19.0 19.0 19.0 20.5 20.5 20.5 22.0 22.0 22.0

Distance from points to the model (3/4)

rr measure_distance <- function(mod, data) { # purpose: calculate distance from observed points to model predictions # inputs: ### mod: vector of parameter estimates for linear model (e.g. intercept & slope(s)) ### data: observed data

diff <- data$y - model1(mod, data) sqrt(mean(diff ^ 2)) } # show RMS deviation for the model measure_distance(c(7, 1.5), sim1)

[1] 2.665212

Distance from points to each candidate models (4/4)

rr sim1_dist <- function(b0, b1) { # purpose: helper function because our distance function # expects the model as a numeric vector of length 2 # inputs: ### b0: intercept estimate ### b1: slope estimate ### sim1: data in environment measure_distance(c(b0, b1), sim1) } models <- models %>% mutate(dist = purrr::map2_dbl(b0, b1, .f = sim1_dist)) models

Progress…

  • We now have a distance metric that quantifies performance of all 250 of our random models
  • Now we can start to evaluate if some of them are useful

rr sim1 %>% ggplot(aes(x, y)) + geom_abline(aes(intercept = b0, slope = b1), data = models, alpha = 0.25) + geom_point()

Best (random) models for sim1 data

rr sim1 %>% ggplot(aes(x, y)) + geom_point(size = 2, colour = 30) + geom_abline(aes(intercept = b0, slope = b1, colour = -dist), data = filter(models, rank(dist) <= 10))

Best (random) models for sim1 data

rr ggplot(models, aes(b0, b1)) + geom_point(data = filter(models, rank(dist) <= 10), size = 4, colour = ) + geom_point(aes(colour = -dist))

Random models was silly… but not that silly

Systematic search cont’d

rr # create systematic grid; fit all models to the data grid <- expand.grid(b0 = seq(0, 7, length = 50), b1 = seq(1.5, 2.5, length = 50)) %>% mutate(dist = purrr::map2_dbl(b0, b1, sim1_dist)) # plot models as points; color
grid %>% ggplot(aes(b0, b1)) + geom_point(data = filter(grid, rank(dist) <= 10), size = 4, colour = ) + geom_point(aes(colour = -dist))

rr sim1 %>% ggplot(aes(x, y)) + geom_point(size = 2, colour = 30) + geom_abline(aes(intercept = b0, slope = b1, colour = -dist), data = filter(grid, rank(dist) <= 10))

optim() and the Newton-Raphson alrogithm

rr nrBest <- optim(par = c(0, 0), fn = measure_distance, data = sim1) # show parameter estimates nrBest$par

[1] 4.222248 2.051204

lm() for linear models

rr sim1_lm <- lm(y ~ x, data = sim1) sim1_lm$coefficients

(Intercept)           x 
   4.220822    2.051533 

Quantifying uncertainty of our estimates

rr # confidence intervals confint(lm(y ~ 1, data = sim1)) # what does this do?

               2.5 %   97.5 %
(Intercept) 13.12483 17.88368

rr confint(sim1_lm, level = 0.95) # what does this do?

               2.5 %   97.5 %
(Intercept) 2.441112 6.000532
x           1.764707 2.338359

rr # model summary table msummary(sim1_lm)

            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.2208     0.8688   4.858 4.09e-05 ***
x             2.0515     0.1400  14.651 1.17e-14 ***

Residual standard error: 2.203 on 28 degrees of freedom
Multiple R-squared:  0.8846,    Adjusted R-squared:  0.8805 
F-statistic: 214.7 on 1 and 28 DF,  p-value: 1.173e-14

Quantifying uncertainty of our estimates

rr nrBest <- optim(par = c(0, 0), fn = measure_distance, data = sim1) nrBest$par

[1] 4.222248 2.051204

rr # note the $par at end of optim()… try without to see why BootstrapModels <- mosaic::do(1000) * optim(par = c(0, 0), fn = measure_distance, data = sample_n(sim1, size = nrow(sim1), replace = TRUE))$par head(BootstrapModels) r # standard error sd(~ V1, data = BootstrapModels) # intercept

[1] 0.9533629

rr sd(~ V2, data = BootstrapModels) # slope

[1] 0.1490552

rr # 95% CI (percentile method) qdata(~ V1, p = c(0.025, 0.975), data = BootstrapModels) # intercept

rr qdata(~ V2, p = c(0.025, 0.975), data = BootstrapModels) # slope

Compare lm() result to Bootstrap

  • Note Standard Error estimates for each coefficient
  • compare confidence interval estimates
  • Q: what if we run the simulation again?
  • Q: what if we run more bootstrap models?

rr # recall our lm() model for comparison msummary(sim1_lm)

            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.2208     0.8688   4.858 4.09e-05 ***
x             2.0515     0.1400  14.651 1.17e-14 ***

Residual standard error: 2.203 on 28 degrees of freedom
Multiple R-squared:  0.8846,    Adjusted R-squared:  0.8805 
F-statistic: 214.7 on 1 and 28 DF,  p-value: 1.173e-14

rr confint(sim1_lm)

               2.5 %   97.5 %
(Intercept) 2.441112 6.000532
x           1.764707 2.338359

Exercise: Mean Absolute Deviation

Evaluating model fit

Model predictions

Initialize the grid

rr grid <- sim1 %>% data_grid(x) # just a bunch of evenly spaced hypothetical values for X… head(grid)

Add some predictions using our existing model (sim1_lm)

rr grid <- grid %>% add_predictions(sim1_lm) # predictions accompanying each hypothetical X value in our grid head(grid)

Plot it!

  • Admittedly, this isn’t the most direct way to add a regression line to a plot (e.g., geom_abline())
  • The benefit: this simple approach extends to just about ANY model in R (even complex models)
  • Your primary limitation now is visualization skills (NOT model complexity)

rr # just the data ggplot(sim1, aes(x)) + geom_point(aes(y = y))

rr # add the grid of predictions to the data ggplot(sim1, aes(x)) + geom_point(aes(y = y)) + geom_point(aes(y = pred), data = grid, colour = , size = 3)

rr # since the model family is linear… ggplot(sim1, aes(x)) + geom_point(aes(y = y)) + geom_line(aes(y = pred), data = grid, colour = , size = 1)

Residuals

Residuals

rr sim1 <- sim1 %>% add_predictions(sim1_lm) %>% add_residuals(sim1_lm) # we’ve just appended the model residuals to our data set head(sim1) r # Let’s look at the distribution of residuals sim1 %>% ggplot(aes(resid)) + geom_histogram(binwidth = 0.5)

rr # or commonly with a density plot sim1 %>% ggplot(aes(resid)) + geom_density()

Residuals vs X (our predictor variable)

  • Q: What do we learn here?

rr sim1 %>% ggplot(aes(x, resid)) + geom_hline(yintercept = 0, linetype = 2) + geom_point()

Formula syntax

Modeling categorical variables

rr # inspect data structures str(sim2)

Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   40 obs. of  2 variables:
 $ x: chr  \a\ \a\ \a\ \a\ ...
 $ y: num  1.94 1.18 1.24 2.62 1.11 ...

rr # show scatter plot of categorical x variable sim2 %>% ggplot() + geom_point(aes(x, y))

rr # fit a simple model and generate predictions again mod2 <- lm(y ~ x, data = sim2) grid <- sim2 %>% data_grid(x) %>% add_predictions(mod2) grid

Modeling categorical variables

rr sim2 %>% ggplot(aes(x)) + geom_point(aes(y = y)) + geom_point(data = grid, aes(y = pred), colour = , size = 4)

Modeling with interactions

rr # inspect data structures str(sim3)

Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   120 obs. of  5 variables:
 $ x1 : int  1 1 1 1 1 1 1 1 1 1 ...
 $ x2 : Factor w/ 4 levels \a\,\b\,\c\,\d\: 1 1 1 2 2 2 3 3 3 4 ...
 $ rep: int  1 2 3 1 2 3 1 2 3 1 ...
 $ y  : Named num  -0.571 1.184 2.237 7.437 8.518 ...
  ..- attr(*, \names\)= chr  \a\ \a\ \a\ \b\ ...
 $ sd : num  2 2 2 2 2 2 2 2 2 2 ...

rr # show scatter plot of categorical x variable sim3 %>% ggplot() + geom_point(aes(x = x1, y = y, color = x2))

rr # fit a simple model and generate predictions again mod3_1 <- lm(y ~ x1 + x2, data = sim3) mod3_2 <- lm(y ~ x1 + x2 + x1*x2, data = sim3) # generate model predictions grid <- sim3 %>% data_grid(x1, x2) %>% add_predictions(mod3_1, var = 1) %>% add_predictions(mod3_2, var = 2) grid

Plot the interaction model

  • we should stack (i.e. gather) the predictions so we can make the model comparisons on the same plot
  • Q: What’s happening here? recall…
    • mod3_1 <- lm(y ~ x1 + x2, data = sim3)
    • mod3_2 <- lm(y ~ x1 + x2 + x1*x2, data = sim3)
    • interpretation of interaction

rr gridStack <- grid %>% gather(key = , value = , mod1, mod2) # model plot sim3 %>% ggplot(aes(x = x1, y = y, color = x2)) + geom_point() + geom_line(data = gridStack, aes(y = pred)) + facet_wrap(~ model)

rr # same model, but facets separate each model & x2 combination sim3 %>% ggplot(aes(x = x1, y = y, color = x2)) + geom_point() + geom_line(data = gridStack, aes(y = pred)) + facet_grid(model ~ x2)

Write down each model

  • many people understand the interaction better by writing down the models
  • here’s the model output so we can work them out

rr summary(mod3_1)


Call:
lm(formula = y ~ x1 + x2, data = sim3)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.4674 -0.8524 -0.0729  0.7886  4.3005 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.87167    0.38738   4.832 4.22e-06 ***
x1          -0.19674    0.04871  -4.039 9.72e-05 ***
x2b          2.88781    0.39571   7.298 4.07e-11 ***
x2c          4.80574    0.39571  12.145  < 2e-16 ***
x2d          2.35959    0.39571   5.963 2.79e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.533 on 115 degrees of freedom
Multiple R-squared:  0.5911,    Adjusted R-squared:  0.5768 
F-statistic: 41.55 on 4 and 115 DF,  p-value: < 2.2e-16

rr summary(mod3_2)


Call:
lm(formula = y ~ x1 + x2 + x1 * x2, data = sim3)

Residuals:
     Min       1Q   Median       3Q      Max 
-2.87634 -0.67655  0.04837  0.69963  2.58607 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.30124    0.40400   3.221  0.00167 ** 
x1          -0.09302    0.06511  -1.429  0.15587    
x2b          7.06938    0.57134  12.373  < 2e-16 ***
x2c          4.43090    0.57134   7.755 4.41e-12 ***
x2d          0.83455    0.57134   1.461  0.14690    
x1:x2b      -0.76029    0.09208  -8.257 3.30e-13 ***
x1:x2c       0.06815    0.09208   0.740  0.46076    
x1:x2d       0.27728    0.09208   3.011  0.00322 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.024 on 112 degrees of freedom
Multiple R-squared:  0.8221,    Adjusted R-squared:  0.811 
F-statistic: 73.93 on 7 and 112 DF,  p-value: < 2.2e-16

Plot the model (mosaic::plotModel())

rr # reproduce our model plots plotModel(mod3_1) # no interaction (parallel lines)

rr plotModel(mod3_2) # interaction (non-parallel…slope can vary by group)

rr # residuals vs fitted values mplot(mod3_1, which = 1)

[[1]]

rr mplot(mod3_2, which = 1)

[[1]]

rr # what’s this? mplot(mod3_2, which = 7)

[[1]]

Interactions (continuous:continuous)

Oridinary Least Squares (OLS) Regression Summary

OLS Regression Fail

Problem: lots of interesting questions can’t be answered by data that satisfy those conditions.

Consider a couple of examples:

  • since the objective is ultimately to associate the probability of success, \(p\), with the covariates, one might be tempted to try a linear model for the average binary response, \(p\): \(p_{i} = \beta_{0} + \beta_{1}x_{i}\)
  • Q: What’s the OLS violation?
  • e.g., the response variable is binary, which violates the OLS assumption of a normally distributed response at each level of X
  • Result: \(p_{i} = \beta_{0} + \beta_{1}x_{i}\) doesn’t work well for bernoulli/binomial response
  • Binomial Regression

OLS Regression Fail

Problem: lots of interesting questions can’t be answered by data that satisfy those conditions.

Consider a couple of examples:

  • one might be tempted to try and model \(\lambda_{i}\) as a linear function of the explanatory variable: \(\lambda_{i} = \beta_{0} + \beta_{1}x_{i}\)
  • Q: What’s the OLS violation?
  • e.g., the response variable is Poisson so we expect that mean = variance = \(\lambda\), which violates the OLS assumption of equal variance for all levels of X
  • Result: \(\lambda_{i} = \beta_{0} + \beta_{1}x_{i}\) doesn’t work well for Poisson response
  • Poisson Regression

OLS Regression Fail

Researchers Interests Variable Type
Ecologists: Number of Species Poisson
Criminologists: Arrest Count Poisson
Cancer specialist: Number of cases Poisson
Political Scientists: Who’s a democrate? Bernoulli or Binomial
Pre-med Student: Who gets into med school? Bernoulli or Binomial
Sociologist: Who’s got a tattoo? Bernoulli or Binomial

Generalized Linear Models (GLMs)

One-parameter exponential family

  • In the early 1970s Nelder & Wedderburn identified a broader class of models that generalizes the multiple linear regression approach under certain conditions:
    1. The probability formula (pdf or pmf) can be written as follows:

    \[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]

    1. the set of possible values (i.e. the support) does not depend upon a parameter
  • If so, it is said to have one-parameter exponential family form


…Here’s the cool part

  • \(\mu = -\frac{c'(\theta)}{b'(\theta)}\)
  • \(\sigma^2 = \frac{b''(\theta)c'(\theta) - c''(\theta)b'(\theta)}{[b'(\theta)]^3}\)
  • (drum roll…)
  • \(b(\theta) = \beta_0 + \beta_1X + ...\) is a linear model!
  • \(b(\theta)\) is called the canonical link function, meaning it’s often a good choice to model as a linear function of the explanatory variables (but other link functions exist and might be preferred under special circumstances).

GLM gut-check (Poisson)

Goal:

\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]

What if our response variable is…

Poisson?

  • support: since possible values for any Poisson random variable range from \(0\) to \(\infty\), the support doesn’t depend on \(\lambda\)
  • \(P(Y=y) = \frac{e^{-\lambda}\lambda^y}{y!}\)
  • since \(\lambda^y = e^{ylog(\lambda)}\)
  • \(P(Y=y) = e^{-\lambda}e^{ylog(\lambda)}e^{-log(y!)}\)
  • \(P(Y=y) = e^{ylog(\lambda) - \lambda - log(y!)}\)
  • then, \(b(\theta) = log(\lambda)\)

GLM gut-check (Binomial)

Goal:

\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]

What if our response variable is…

Binomial?

  • support: for any value of \(p\), \(0 < p < 1\), all integer values from 0 to \(n\) are possible so the support doesn’t depend on \(p\).
  • \(P(Y=y) = {n\choose{y}}p^y(1-p)^{n-y}\)
  • \(P(Y=y) = e^{ylog(p) + (n-y)log(1-p) + log{n\choose{y}}}\)
  • \(P(Y=y) = e^{ylog(\frac{p}{1-p}) + nlog(1-p) + log{n\choose{y}}}\)
  • then, \(b(\theta) = log(\frac{p}{1-p})\)
  • this is called a logit function, and it is interpreted as the log of the odds of “success” where the observed outcome is deemed either “success” or “failure”

GLM gut-check (Normal)

Goal:

\[f(y; \theta)=exp^{(a(y)b(\theta) + c(\theta) + d(y))}\]

What if our response variable is…

…Normal?

  • \(f(y) = \frac{1}{\sigma\sqrt(2\pi)}e^{-\frac{(y - \mu)^2}{2\sigma^2}}\)
  • \(f(y) = e^{-log(\sigma)-log(2\pi)/2}e^{-\frac{(y - \mu)^2}{2\sigma^2}}\)
  • not looking good for our hero, but don’t panic… what if we assume \(\sigma\) is known (i.e. constant)?
  • \(f(y) \propto e^{-(-2y\mu + \mu^2 + y^2)}\)
  • then, \(b(\theta) = \mu\)
  • so, \(\mu_{Y|X} = \beta_{0} + \beta_{1}X\)
  • and, of course, possible values for any Normal random variable range from \(-\infty\) to \(\infty\), so the support looks good!
  • Does this square with what you already know about multiple regression with a Normal response?

Who’s in the family?

Here are some other distributions that can be written in one-parameter exponential family form:

Poisson Regression

Logistic Regression

One last time… Back to Chris

rr require(lubridate) # some minor wrangling (what are we doing here?) OrdColleagueData <- OrdColleagueData %>% mutate(timelyDepart = if_else(dep_delay < 10, TRUE, FALSE), date = ymd(paste0(year, -, month, -, day)), dow = as.character(wday(date, label = TRUE)))

Plot the relationship

rr OrdColleagueData %>% filter(!is.na(timelyDepart)) %>% ggplot(aes(x = hour, y = as.numeric(timelyDepart))) + geom_jitter(alpha = 0.3, height = 0.05) + geom_smooth(method = , method.args = list(family = ), se = 0) + ylab(-Time Departure Status)

Logistic Regression Modeling

rr logitMod <- glm(timelyDepart ~ hour + dow, family = , data = OrdColleagueData) msummary(logitMod)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)   2.8451     0.7127   3.992 6.56e-05 ***
hour         -0.1490     0.0411  -3.625 0.000289 ***
dowMon        0.5220     0.5593   0.933 0.350661    
dowSat        0.6658     0.6786   0.981 0.326543    
dowSun        0.2946     0.5819   0.506 0.612704    
dowThu       -0.2611     0.5409  -0.483 0.629289    
dowTue        1.4252     0.7329   1.945 0.051833 .  
dowWed        0.5224     0.5813   0.899 0.368810    

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 241.83  on 217  degrees of freedom
Residual deviance: 218.63  on 210  degrees of freedom
  (12 observations deleted due to missingness)
AIC: 234.63

Number of Fisher Scoring iterations: 4

rr confint(logitMod)

Waiting for profiling to be done...
                  2.5 %      97.5 %
(Intercept)  1.51072125  4.32058967
hour        -0.23324773 -0.07126707
dowMon      -0.56796752  1.64527078
dowSat      -0.61479789  2.09876835
dowSun      -0.84017869  1.46041787
dowThu      -1.33438665  0.80045987
dowTue       0.08024228  3.03437106
dowWed      -0.60520070  1.69755918

Other model families

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTW9kZWxpbmcgRm91bmRhdGlvbnMiCnN1YnRpdGxlOiAiTURTUiBDaCA3ICYgUjREUyBDaCAyMi0yMyAmIE1EU1IgQXBwZW5kaXggRSIKb3V0cHV0OiAKICBzbGlkeV9wcmVzZW50YXRpb246IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0IAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGV2YWw9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCiMgb3B0aW9ucyhuYS5hY3Rpb24gPSBuYS53YXJuKQoKIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBsb2FkIGFsbCBwYWNrYWdlcyBoZXJlCmxpYnJhcnkobWRzcikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobW9zYWljKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKbGlicmFyeShtb2RlbHIpCmxpYnJhcnkobHVicmlkYXRlKQoKCiMjIyB1c2VyLWRlZmluZWQgZnVuY3Rpb25zIGhlcmUgKGlmIGFueSkKIyBgbW9kZWwxYCBjYWxjdWxhdGVzIHByZWRpY3Rpb25zIGZvciBhIGdpdmVuIG1vZGVsCiMgYG1lYXN1cmVfZGlzdGFuY2VgIGNhbGN1bGF0ZXMgb3VyIGRpc3RhbmNlIG1ldHJpYyBmb3IgYSBnaXZlbiBtb2RlbAojIGBzaW0xX2Rpc3RgIGhlbHBlciBmdW5jdGlvbiBmb3IgYHB1cnJyOjptYXAyKClgIGV4ZWN1dGlvbgoKIyBpbnB1dHMgKGUuZy4gZGF0YSkgc3VtbWFyeQpkYXRhKCJmbGlnaHRzIikgICMgZnJvbSBgbnljZmxpZ2h0czEzYApkYXRhKCJzaW0xIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW0yIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW0zIikgICAgICMgZnJvbSBgbW9kZWxyYApkYXRhKCJzaW00IikgICAgICMgZnJvbSBgbW9kZWxyYAoKYGBgCgoKIyMgQWdlbmRhCgotIHJlc291cmNlcwogICAgLSBNRFNSIENoYXB0ZXIgNwogICAgLSBSIGZvciBEYXRhIFNjaWVuY2UgQ2hhcHRlcnMgMjItMjMgKDxodHRwczovL3I0ZHMuaGFkLmNvLm56L21vZGVsLWludHJvLmh0bWw+KQotIHN0YXRpc3RpY2FsIG1vZGVsaW5nIGZvdW5kYXRpb25zCiAgICAtIFNhbXBsZXMgJiBwb3B1bGF0aW9ucwogICAgLSBTYW1wbGUgc3RhdGlzdGljcwogICAgLSBCb290c3RyYXBwaW5nCiAgICAtIE91dGxpZXJzCiAgICAtIFJlZ3Jlc3Npb24KCgojIyMjIEFubm91bmNlbWVudHMKCi0gS2VlcCB1cCB3aXRoIFBpYXp6YQotIENhbnZhcyBhc3NpZ25tZW50cyAKICAgIC0gTURTUiBDaGFwIDUgRXhlcmNpc2VzCiAgICAtIE1pbmktUHJvamVjdDogQmlrZSBTaGFyZSBhbmQgTG9jYWwgV2VhdGhlcgogICAgLSBQcm9ncmFtbWluZyBOb3RlYm9va3M6IE1EU1IgQ2hhcHRlciAwNwogICAgICAgIC0gWW91ciByZXN1bHRzIG1heSBkaWZmZXIgZnJvbSB0aGUgYm9vayBpbiBTZWMgNy4yICYgU2VjIDcuMyAoaXQncyBPSykKICAgICAgICAtIGxpa2VseSBgc2V0LnNlZWQoKWAgaXNzdWUsIHNvIHJhbmRvbS9zaW11bGF0ZWQgcHJvY2Vzc2VzIGRpZmZlcgoKCiMjIFN0YXRpc3RpY2FsIEZvdW5kYXRpb25zCgotICoqR29hbDogZXh0cmFjdCAqbWVhbmluZyogZnJvbSBkYXRhKiogIAotIENvbW1vbiBwaXRmYWxsLi4uCiAgICAtIGRvbid0ICp1bmRlcmVzdGltYXRlKiBpbXBvcnRhbmNlIG9mIHZpc3VhbGl6YXRpb24gCiAgICAtIGRvbid0ICpvdmVyZXN0aW1hdGUqIGltcG9ydGFuY2Ugb2YgYW5hbHl0aWNhbCByZXN1bHRzIAogICAgLSBib3RoIGhhdmUgaW1wb3J0YW50IGFuZCBjb21wbGltZW50YXJ5IHJvbGVzIAoKIVtJbWFnZSBjcmVkaXQ6IFIgZm9yIERhdGEgU2NpZW5jZSAoaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9tb2RlbC1pbnRyby5odG1sKV0ocjRkc19zMjItMC5wbmcpCgoKIyMgS2V5IGlkZWFzOiBTYW1wbGVzLCBwb3B1bGF0aW9ucywgc2FtcGxpbmcgZGlzdHJpYnV0aW9ucwoKLSBvdXIgZGF0YSBhcmUganVzdCBhICoqc2FtcGxlKiogcmVwcmVzZW50YXRpdmUgb2Ygc29tZSBsYXJnZXIgcG9wdWxhdGlvbiAob3IgcHJvY2VzcykKICAgIC0gdGhlIHNhbXBsZSBpcyAqcmFuZG9tKgogICAgLSBpZiBhbm90aGVyIHBlcnNvbiB3ZXJlIHRvIHJlcGxpY2F0ZSB0aGUgc3R1ZHksIHRoZXkgd291bGQgZ2V0IGEgZGlmZmVyZW50IHNhbXBsZQogICAgLSBpZiB3ZSB3ZXJlIHRvIGRvIHRoZSBzdHVkeSBhZ2FpbiBpbiB0aGUgZnV0dXJlLCB3ZSB3b3VsZCBnZXQgYSBkaWZmZXJlbnQgc2FtcGxlCi0gd2UgY2FyZSBhYm91dCBwYXR0ZXJucy9zdHJ1Y3R1cmUvbW9kZWxzIG9mIHRoZSAqKnBvcHVsYXRpb24qKgogICAgLSBhIGdvb2QgbW9kZWwgaW5jbHVkZXMgc2FsaWVudCBmZWF0dXJlcyBvZiB0aGUgcG9wdWxhdGlvbiB0byByZXN1bHQgaW4gc2ltaWxhciBvdXRjb21lcyBhY3Jvc3MgKHJhbmRvbSkgc2FtcGxlcwotIHRoZSAqKnNhbXBsaW5nIGRpc3RyaWJ1dGlvbioqIGlzIHNpbXBseSBhIGRpc3RyaWJ1dGlvbiBvZiBwbGF1c2libGUgb3V0Y29tZXMgdGhhdCB3b3VsZCBiZSBvYnNlcnZlZCBpZiB0aGUgc3R1ZHkgd2VyZSByZXBlYXRlZCBpbmZpbml0ZWx5IG1hbnkgdGltZXMuCiAgICAtIGJhc2ljYWxseSwgdGhpcyBpcyBhIGh5cG90aGV0aWNhbCBkaXN0cmlidXRpb24gb2YgYSBzYW1wbGUgc3RhdGlzdGljIGFtb25nIGFsbCBwb3NzaWJsZSBzYW1wbGVzIG9mIGEgZ2l2ZW4gc2l6ZQogICAgLSB0aGlzIHByaW1hcmlseSBnaXZlcyB1cyBhIHNlbnNlIG9mIHRoZSAqdmFyaWFiaWxpdHkqIHRvIGJlIGV4cGVjdGVkIGZyb20gb25lIHNhbXBsZSB0byBhbm90aGVyCgoKIyMgU3RhdGlzdGljYWwgRm91bmRhdGlvbnMKCi0gd2Ugb2Z0ZW4gZG9uJ3QgYWN0dWFsbHkgY2FyZSB0aGF0IG11Y2ggYWJvdXQgdGhlIHNwZWNpZmljIHNhbXBsZSB0aGF0IHdlIGhhdmUKICAgIC0gYSBzYW1wbGUgaXMgYSBvbmUtdGltZSByZWFsaXphdGlvbiBvZiBzb21lIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzCiAgICAtIGlmIHdlIHJlcGVhdCB0aGUgc3R1ZHkvaW52ZXN0aWdhdGlvbiBhbmQgY29sbGVjdCBuZXcgZGF0YSB3ZSBhbG1vc3QgY2VydGFpbmx5IG9ic2VydmUgYSBkaWZmZXJlbnQgc2FtcGxlCiAgICAtIGlmIHdlIGNhbid0IG1ha2UgY29uY2x1c2lvbnMgKmJleW9uZCogdGhlIHNwZWNpZmljIHNhbXBsZSB3ZSBoYXBwZW5lZCB0byBlbmNvdW50ZXIsIHdlIHR5cGljYWxseSBoYXZlbid0IGdhaW5lZCBtdWNoCi0gd2UgdHlwaWNhbGx5IGNhcmUgZmFyIG1vcmUgYWJvdXQgd2hhdCB0aGUgc2FtcGxlICoqcmVwcmVzZW50cyoqIChhbmQgaXQncyBsaW1pdGF0aW9ucykKICAgIC0gZXhwbG9yYXRpb24gb3IgZXhwbGFuYXRpb24gb2Ygc29tZSBsYXJnZXIgcG9wdWxhdGlvbiBvciBwcm9jZXNzCiAgICAtIG91dGNvbWVzL3JlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEgdGhhdCBjYW4gYmUgcmVwbGljYXRlZC9yZXByb2R1Y2VkCiAgICAtIGZ1dHVyZSBwcmVkaWN0aW9ucwogICAgLSBhbmQgY2FyZWZ1bGx5IHF1YW50aWZ5aW5nIHVuY2VydGFpbnR5IG9mIHRoZSBvdXRjb21lcyBhYm92ZQoKCiMjIEV4YW1wbGU6IG1lZXQgQ2hyaXMKCiFbXShjaHJpcy5wbmcpCgotIE1vdGl2YXRpb246IAogICAgLSBBIGNsb3NlIGZyaWVuZCBvZiBtaW5lIGlzIGFuIGV4ZWN1dGl2ZSBtYW5hZ2VtZW50IGNvbnN1bHRhbnQgd2l0aCBQd0MKICAgIC0gQ2hyaXMgbGl2ZXMgb24gdGhlIEVhc3QgQ29hc3QsIGJ1dCB0cmF2ZWxzIChNLVRoKSBldmVyeSB3ZWVrIGZvciB3b3JrCiAgICAtIEhpcyBwcmltYXJ5IGNsaWVudHMgKHJpZ2h0IG5vdykgYXJlIG9uIFdlc3QgQ29hc3QgJiBNaWR3ZXN0LgogICAgLSBGbGlnaHQgZGVsYXlzIGFyZSBhIG1ham9yIHRocmVhdCwgYmVjYXVzZSBpZiB0aGUgY2xpZW50IGNhbid0IHJlbHkgb24gaGltIHRvIGJlIHRoZXJlIG9uIHRpbWUgdGhleSBtYXkgZ2l2ZSB0aGUgcHJvamVjdCB0byBhIGRpZmZlcmVudCBjb25zdWx0YW50LgotICoqR29hbDogSGUgd2FudHMgdG8gbWF4aW1pemUgdGltZSBhdCBob21lIHdpdGggaGlzIHdpZmUgYW5kIGtpZHMsIHdpdGhvdXQgamVvcGFyZGl6aW5nIHdvcmsgY29tbWl0bWVudHMqKgogICAgLSBIb3cgZWFybHkgc2hvdWxkIENocmlzIHNob3VsZCBwbGFuIHRvIGFycml2ZSBpbiBDaGljYWdvIChPUkQpIGlmIGhlIHdhbnRzIHRvIGF2b2lkIG1pc3NpbmcgbWVldGluZ3MgZHVlIHRvIGZsaWdodCBkZWxheXM/CiAgICAtIFE6IElkZWFzIGhvdyB0byBhcHByb2FjaCB0aGUgcHJvYmxlbT8KICAgIAoKYGBge3IgZXZhbD1UUlVFfQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZShtZHNyKQpyZXF1aXJlKG1vc2FpYykKcmVxdWlyZShueWNmbGlnaHRzMTMpCgpkYXRhKGZsaWdodHMpCgojIEdldCBmbGlnaHRzIGZyb20gTllDIHRvIE9SRApPcmREZWxheXMgPC0gCiAgZmxpZ2h0cyAlPiUKICBmaWx0ZXIoZGVzdCA9PSAiT1JEIikKYGBgCgoKCiMjIEV4YW1wbGU6IGZsaWdodCBkZWxheXMgZnJvbSBOZXcgWW9yayB0byBDaGljYWdvCgpgYGB7ciBldmFsPVRSVUV9Ck9yZERlbGF5czI4IDwtIAogIE9yZERlbGF5cyAlPiUKICBzYW1wbGVfbihzaXplID0gMjgpCmBgYAoKLSBDaHJpcyBmbGllcyBhIGxvdCBhbmQga25vd3Mgc29tZSBzdGF0aXN0aWNzLCBidXQgbGV0cyBzdXBwb3NlIGhlIG9ubHkgaGFzIGRhdGEgZnJvbSAyOCBmbGlnaHRzCiAgICAtIEhlcmUgYXJlIHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgICAtIFE6IEhvdyBzaG91bGQgaGUgZGVjaWRlIHdoZW4gdG8gYXJyaXZlPwoKYGBge3IgZXZhbD1UUlVFfQojIHN1bW1hcnkgc3RhdGlzdGljcyBvZiBkZWxheXMKZmF2c3RhdHMoIH4gYXJyX2RlbGF5LCBkYXRhID0gT3JkRGVsYXlzMjgpCmBgYAoKIyMgRXhhbXBsZTogZmxpZ2h0IGRlbGF5cyBmcm9tIE5ldyBZb3JrIHRvIENoaWNhZ28KCi0gVHJhZGUtb2ZmLi4uIE1heWJlIENocmlzIGNhbiByaXNrIGJlaW5nIGxhdGUgb25jZSBpbiBhIHdoaWxlIHRvIGhhdmUgbW9yZSB0aW1lIHdpdGggaGlzIGZhbWlseQotIEhlcmUncyB3aGF0IGhlIG1pZ2h0IGRlY2lkZSAqYmFzZWQgb24gaGlzIHNhbXBsZSoKICAgIC0gaGUgZmxpZXMganVzdCBhYm91dCBldmVyeSB3ZWVrCiAgICAtIDk3JSBwZXJjZW50IG9uLXRpbWUgd291bGQgbWVhbiB0aGF0IGhlIGV4cGVjdHMgdG8gYmUgbGF0ZSBhIGxpdHRsZSBtb3JlIHRoYW4gb25jZSBwZXIgeWVhci4KCmBgYHtyIGV2YWw9VFJVRX0KdG9sZXJhbmNlMjggPC0gcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gT3JkRGVsYXlzMjgpCnRvbGVyYW5jZTI4CgpgYGAKCiMjIEV4YW1wbGU6IGZsaWdodCBkZWxheXMgZnJvbSBOZXcgWW9yayB0byBDaGljYWdvCgotIENocmlzIGRvZXNuJ3QgaGF2ZSB0aGUgKipwb3B1bGF0aW9uKioKICAgIC0gaGUgY2FuJ3Qga25vdyBpZiBoaXMgaXMgYW4gZWZmZWN0aXZlIHBvbGljeSBvciBub3QuLi4gCiAgICAtIGRvZXMgdGhpcyAqKnJlYWxseSoqIHdvcmsgOTclIG9mIHRoZSB0aW1lPz8gIAogICAgLSBJcyBoZSB0b28gbGF0ZT8gIFRvbyBlYXJseT8/Ci0gSW4gb3VyIGh5cG90aGV0aWNhbCBleGVyY2lzZSwgd2UgKipkbyoqIGhhdmUgYSBwb3B1bGF0aW9uLCAKICAgIC0gd2UgY2FuIHNlZSBob3cgaGUgZGlkIAoKYGBge3IgZXZhbD1UUlVFfQojIHJlYWxpdHkgb2YgcG9wdWxhdGlvbiAodW5rbm93bi91bmtub3dhYmxlIHRvIENocmlzKQpmYXZzdGF0cyggfiBhcnJfZGVsYXksIGRhdGEgPSBPcmREZWxheXMpICAjIGZ1bGwgZGF0YQoKIyBwb3B1bGF0aW9uIHZhbHVlIGZvciAwLjk3IHF1YW50aWxlCnRvbGVyYW5jZSA8LSBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIGRhdGEgPSBPcmREZWxheXMpICAjIGZ1bGwgZGF0YQp0b2xlcmFuY2UKCiMgQWN0dWFsIHBlcmZvcm1hbmNlIGZvciBDaHJpcy4uLgp0YWxseSh+IGFycl9kZWxheSA8IHRvbGVyYW5jZTI4WzJdLCBkYXRhID0gT3JkRGVsYXlzLCBmb3JtYXQgPSAicHJvcG9ydGlvbiIpCmBgYAoKCgojIyBQcm9ibGVtIHNvbHZlZD8gIChOb3QgcXVpdGUpCgotIFRoZXJlJ3Mgc3RpbGwgcXVpdGUgYSBiaXQgb2YgcmlzayBkdWUgdG8gcmFuZG9tbmVzcy4uLiAKICAgIC0gYW4gaW50ZXJ2YWwgZXN0aW1hdGUgZm9yIHRoZSAwLjk3IHF1YW50aWxlIHdvdWxkIGJlIGJldHRlcgogICAgLSBROiAqKmhvdyBzaG91bGQgd2UgZG8gaXQ/KioKCmBgYHtyIGV2YWw9VFJVRX0KIyBSZXN1bHRzIHZhcnkuLi4gZm91ciBkaWZmZXJlbnQgc2FtcGxlcyA9IGZvdXIgZGlmZmVyZW50IHJlc3VsdHMKbiA8LSAyOAoKcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbiwgcmVwbGFjZSA9IFRSVUUpKQpxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIGRhdGEgPSBzYW1wbGVfbihPcmREZWxheXMsIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkpCnFkYXRhKH4gYXJyX2RlbGF5LCBwID0gMC45NywgZGF0YSA9IHNhbXBsZV9uKE9yZERlbGF5cywgc2l6ZSA9IG4sIHJlcGxhY2UgPSBUUlVFKSkKcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbiwgcmVwbGFjZSA9IFRSVUUpKQpgYGAKCiMjIERpc3RyaWJ1dGlvbiBvZiB0aGUgZXN0aW1hdGUgZGVwZW5kcyBvbiBzYW1wbGUgc2l6ZQoKLSBMZXQncyBzaW11bGF0ZSAxMDAwIHNhbXBsZXMgZnJvbSBvdXIgYE9yZERlbGF5YCAoZnVsbCkgZGF0YQotIEFueSBvbmUgb2YgdGhlc2UgY291bGQgYmUgdGhlIHNhbXBsZSB0aGF0IENocmlzIGhhcHBlbmVkIHRvIG9ic2VydmUKLSBXZSBqdXN0IHdhbnQgdG8gc2VlIGhvdyBtdWNoIHRoZXNlIHNhbXBsZXMgdmFyeSBmcm9tIG9uZSB0byB0aGUgbmV4dAoKYGBge3IgZXZhbD1UUlVFfQpuIDwtIDI4CgpTaW1zU21hbGxOIDwtIAogIG1vc2FpYzo6ZG8oMTAwMCkgKiBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzYW1wbGVfbihPcmREZWxheXMsIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkpCgojIGluc3BlY3QgcmVzdWx0CmhlYWQoU2ltc1NtYWxsTikKCiMgdmFyaWFiaWxpdHkgb2Ygb3VyIDAuOTcgcXVhbnRpbGUgZXN0aW1hdGUgKG5vdGUgInNkIikKZmF2c3RhdHMofiBxdWFudGlsZSwgZGF0YSA9IFNpbXNTbWFsbE4pCmBgYAoKIyMjIyBWb2NhYnVsYXJ5CgotICpTYW1wbGUgc2l6ZSAobikqIGlzIHRoZSBudW1iZXIgb2YgY2FzZXMgaW4gYW4gb2JzZXJ2ZWQgc2FtcGxlCi0gKlNhbXBsaW5nIGRpc3RyaWJ1dGlvbiogaXMgdGhlIGNvbGxlY3Rpb24gb2YgdGhlIHNhbXBsZSBzdGF0aXN0aWMgZnJvbSBhbGwgdHJpYWxzCiAgICAtIGEgdGhlb3JldGljYWwgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGluY2x1ZGVzIGFsbCBwb3NzaWJsZSB0cmlhbHMKICAgIC0gd2UgaGF2ZSAxMDAwIHNpbXVsYXRlZCB0cmlhbHMsIAogICAgICAgIC0gdGhhdCBzcGVjaWZpYyBudW1iZXIgZG9lc24ndCBtYXR0ZXIgbXVjaCBhcyBsb25nIGFzIGl0J3MgImxhcmdlIgogICAgICAgIC0gMTBzIG9mIHRob3VzYW5kcyBvciBtaWxsaW9ucyBhcmUgY29tbW9uIGluIHByYWN0aWNlCiAgICAgICAgLSBkbyBtb3JlIGlmIGEgcHJlY2lzZSByZXN1bHQgaXMgaW1wb3J0YW50Ci0gKnNoYXBlKiBvZiB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGlzIHdvcnRoIG5vdGluZyAob3VycyBpcyByaWdodC1za2V3ZWQgaGVyZSkKLSAqc3RhbmRhcmQgZXJyb3IqIGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBmb3IgYSBzdGF0aXN0aWMKICAgIC0gSGVyZSwgd2UgZXN0aW1hdGUgdGhlIFNFIHVzaW5nIHRoZSBTRCBvZiBtYW55IHNpbXVsYXRlZCAwLjk3IHF1YW50aWxlIGVzdGltYXRlcwogICAgLSB5b3UgY2FuIGZpbmQgdGhpcyByZXN1bHQgaW4gb3VyIGBmYXZzdGF0cygpYCBvdXRwdXQgYWJvdmUKCmBgYHtyIGV2YWw9VFJVRX0KU2ltc1NtYWxsTiAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gcXVhbnRpbGUpKSArIAogIGdndGl0bGUoIjEwMDAgc2ltdWxhdGVkIDAuOTcgcXVhbnRpbGVzIGZvciBmbGlnaHQgZGVsYXlzIGFtb25nIHNhbXBsZXMgb2YgbiA9IDI4IikKYGBgCgoKIyMgV2hhdCBpZiB3ZSBoYXZlIGEgYmlnZ2VyIHNhbXBsZT8KCi0gTWF5YmUgQ2hyaXMgY2FuIHBvb2wgZmxpZ2h0IGluZm9ybWF0aW9uIHdpdGggYSBmZXcgY29sbGVhZ3VlcyB0aGF0IHJlZ3VsYXJseSBmbHkgTllDIHJvIE9SRAogICAgLSBObyBmbGlnaHQgY291bnRlZCBtb3JlIHRoYW4gb25jZSAoaS5lLiBpZiB0aGV5IGhhZCBzaGFyZWQgYSBmbGlnaHQpCiAgICAtIFE6IFdvdWxkIGEgYmlnZ2VyIGRhdGEgc2V0IG1hdHRlcj8gIFdoYXQgZG8geW91IGV4cGVjdCB0byBjaGFuZ2U/CgpgYGB7ciBldmFsPVRSVUV9Cm5CaWdnZXIgPC0gMTUwCgpTaW1zQmlnTiA8LSAKICBtb3NhaWM6OmRvKDEwMDApICogcWRhdGEofiBhcnJfZGVsYXksIHAgPSAwLjk3LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gc2FtcGxlX24oT3JkRGVsYXlzLCBzaXplID0gbkJpZ2dlciwgcmVwbGFjZSA9IFRSVUUpKQoKIyBpbnNwZWN0IHJlc3VsdApoZWFkKFNpbXNTbWFsbE4pCgpgYGAKCiMjIyMgUGxvdCBjb21wYXJpc29uIChuID0gMjg7IG4gPSAxNTApCgpgYGB7ciBldmFsPVRSVUV9CiMgY29tYmluZSByZXN1bHRzIGludG8gYSBzaW5nbGUgdGJsCkJvdGhTaW1zIDwtIAogIGJpbmRfcm93cyhTaW1zU21hbGxOICU+JSBtdXRhdGUoU2FtcGxlU2l6ZSA9IG4pLCBTaW1zQmlnTiAlPiUgbXV0YXRlKFNhbXBsZVNpemUgPSBuQmlnZ2VyKSkgCgojIHN1bW1hcnkgY29tcGFyaXNvbgpmYXZzdGF0cyhxdWFudGlsZSB+IFNhbXBsZVNpemUsIGRhdGEgPSBCb3RoU2ltcykKCiMgcGxvdCBjb21wYXJpc29uCkJvdGhTaW1zICU+JQogIGdncGxvdChhZXMoeCA9IHF1YW50aWxlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsgCiAgZmFjZXRfZ3JpZCggfiBTYW1wbGVTaXplKSArCiAgeGxhYigiU2FtcGxlIDAuOTggUXVhbnRpbGUiKQpgYGAKCgoKIyMgQWNjZXNzIHRvIFNhbXBsaW5nIERpc3RyaWJ1dGlvbgoKLSBJbXBvcnRhbnQ6IGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSB3ZSB3ZXJlIGFibGUgdG8gc2ltdWxhdGUgYW4gYWN0dWFsIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBiZWNhdXNlIHdlIHdlcmUgYWN0dWFsbHkgZHJhd2luZyByYW5kb20gc2FtcGxlcyBmcm9tIHRoZSBwb3B1bGF0aW9uLiAgCiAgICAtIFdlIG5lYXJseSBhbHdheXMganVzdCBoYXZlIG9uZSBzYW1wbGUKICAgIC0gV2UgbmVlZCBhbm90aGVyIHdheSB0byBhY2Nlc3MgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbgotIFdpdGgganVzdCBvbmUgc2FtcGxlIG9mIHRoZSBkYXRhIHdlIGhhdmUgYSBmZXcgb3B0aW9ucyB0byBjaGFyYWN0ZXJpemUgYSBzYW1wbGluZyBkaXN0cmlidXRpb24KICAgIC0gQW5hbHl0aWNhbCBzb2x1dGlvbnMgZGVyaXZlZCBmcm9tIHN0YXRpc3RpY2FsIHRoZW9yeSAoZS5nLiB0LXRlc3QpCiAgICAtIFNpbXVsYXRpb24tYmFzZWQgbWV0aG9kcwoKCiMjIEJvb3RzdHJhcHBpbmcKCi0gV2UgYXNzdW1lIG91ciBzYW1wbGUgaXMgcmVwcmVzZW50YXRpdmUgb2YgdGhlIHBvcHVsYXRpb24KLSBUaGUgcG9wdWxhdGlvbiBjYW4gYmUgcmVhc29uYWJseSBhcHByb3hpbWF0ZWQgYnkgbWFueSwgbWFueSwgbWFueSBjb3BpZXMgb2Ygb3VyIHNhbXBsZQotICoqSnVzdCBhcyB3ZSBoYWQgcHJldmlvdXNseSBkcmF3biBtYW55IHNhbXBsZXMgZnJvbSB0aGUgcG9wdWxhdGlvbiwgImJvb3RzdHJhcHBpbmciIGFwcHJveGltYXRlcyB0aGlzIHByb2Nlc3MgYnkgZHJhd2luZyBzaW11bGF0ZWQgc2FtcGxlcyAod2l0aCByZXBsYWNlbWVudCkgZnJvbSBvdXIgb3JpZ2luYWwgc2FtcGxlKioKCgojIyMjIERpc2N1c3Npb24gUXVlc3Rpb25zOiAKLSBXaHkgZG8gd2UgbmVlZCB0byBzYW1wbGUgKip3aXRoKiogcmVwbGFjZW1lbnQ/Ci0gRG9lcyBib290c3RyYXBwaW5nIGNvbGxlY3QgKipuZXcqKiBkYXRhPwotIERvIHlvdSBleHBlY3QgdGhlICoqbWVhbioqIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHRvIGJlIGVxdWl2YWxlbnQgdG8gdGhlIG1lYW4gb2YgdGhlICh0cnVlKSBzYW1wbGluZyBkaXN0cmlidXRpb24/Ci0gRG8geW91IGV4cGVjdCB0aGUgKipzdGFuZGFyZCBlcnJvcioqIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHRvIGJlIGVxdWl2YWxlbnQgdG8gdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSAodHJ1ZSkgc2FtcGxpbmcgZGlzdHJpYnV0aW9uPwoKCjwhLS0gRGF5MiAtLT4KCiMjIEJhY2sgdG8gQ2hyaXMuLi4KCioqQ2hyaXMgd2FudHMgdG8gYmUgbW9yZSBjb25maWRlbnQgdGhhdCBmbGlnaHQgZGVsYXlzIHdpbGwgbm90IG1ha2UgaGltIGxhdGUgbW9yZSB0aGFuIDMlIG9mIHRoZSB0aW1lLCB3aGF0IHNob3VsZCBoZSBkbz8qKiAgCi0gSGUgcG9vbHMgZmxpZ2h0IGRlbGF5IGRhdGEgd2l0aCBjb2xsZWFndWVzIGFuZCB0aGV5IGNvbWUgdXAgd2l0aCBhIG11Y2ggYmlnZ2VyIGRhdGEgc2V0IG9mIDIzMCBmbGlnaHRzCi0gV2UnbGwgY2FsbCBpdCBgT3JkQ29sbGVhZ3VlRGF0YWAKCiMjIEJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFsCgoxLiB1c2luZyB0aGUgZGF0YSBjb21iaW5lZCBhbW9uZyBDaHJpcycgY29sbGVhZ3Vlcy0tYE9yZENvbGxlYWd1ZURhdGFgLS13ZSBzYW1wbGUgd2l0aCByZXBsYWNlbWVudCBhbmQgY2FsY3VsYXRlIHRoZSAwLjk3IHF1YW50aWxlIGVhY2ggdGltZS4KMi4gd2Ugbm93IGhhdmUgYSBkaXN0cmlidXRpb24gb2YgYm9vdHN0cmFwIHNhbXBsZSBxdWFudGlsZSBlc3RpbWF0ZXMgKDEwMDAgb2YgdGhlbSBpbiB0aGlzIGNhc2UpCjMuIHdlIHVzZSB0aGF0IGRpc3RyaWJ1dGlvbiB0byBwcm9kdWNlIGFuIGludGVydmFsIGVzdGltYXRlIAogICAgLSBJbiB0aGlzIGNhc2UsIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgOTd0aCBwZXJjZW50aWxlIG9mIGFycml2YWwgZGVsYXkKCgojIyMjIGJvb3RzdHJhcCBzYW1wbGluZyBkaXN0cmlidXRpb24KYGBge3J9CiMgQ2hyaXMgYW5kIGNvbGxlYWd1ZXMgcG9vbCBkYXRhIGZvciAyMzAgc2FtcGxlcwpPcmRDb2xsZWFndWVEYXRhIDwtIAogIE9yZERlbGF5cyAlPiUKICBzYW1wbGVfbihzaXplID0gMjMwLCByZXBsYWNlID0gRkFMU0UpCgojIGJvb3RzdHJhcCBkaXN0cmlidXRpb24KQm9vdFN0cmFwVHJpYWxzIDwtIAogIG1vc2FpYzo6ZG8oMTAwMCkgKiBxZGF0YSh+IGFycl9kZWxheSwgcCA9IDAuOTcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gc2FtcGxlX24oT3JkQ29sbGVhZ3VlRGF0YSwgc2l6ZSA9IDIzMCwgcmVwbGFjZSA9IFRSVUUpKQoKYGBgCgojIyMjIGNvbmZpZGVuY2UgaW50ZXJ2YWwKCi0gSWYgdGhlIGJvb3RzdHJhcCBzYW1wbGluZyBkaXN0cmlidXRpb24gbG9va3MgYXBwcm94aW1hdGVseSBOb3JtYWw6IAogICAgLSBjb21wdXRlIHRoZSBDaHJpcydzIHNhbXBsZSBtZWFuCiAgICAtIGVzdGltYXRlIHRoZSBzdGFuZGFyZCBlcnJvciB1c2luZyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBib290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uCiAgICAtIENvbmZpZGVuY2UgaW50ZXJ2YWw6ICRlc3QgXHBtIHpeKiAoU0UpJAogICAgICAgIC0gImVzdCIgaXMgdGhlIGVzdGltYXRlIGZyb20gdGhlIHNhbXBsZSAoQ2hyaXMnIGRhdGEpCiAgICAgICAgLSAkel4qJCBpcyBhIGNyaXRpY2FsIHZhbHVlIGZyb20gdGhlIE5vcm1hbCBkaXN0cmlidXRpb24gY29tbWVuc3VyYXRlIHdpdGggY29uZmlkZW5jZSBsZXZlbCAoZS5nLiAxLjk2IGZvciA5NSUgQ0kpCiAgICAgICAgLSAkU0UkIGlzIHRoZSBzdGFuZGFyZCBlcnJvciB3aGljaCB3ZSd2ZSBlc3RpbWF0ZWQgZnJvbSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uCi0gSWYgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBpcyBmYWlybHkgc21vb3RoLCBidXQgbm90IHN5bW1ldHJpYyAoZS5nLiBza2V3ZWQpLCAKICAgIC0gd2UgY291bGQgdXNlIGEgcGVyY2VudGlsZSBtZXRob2QgdG8gYXBwcm94aW1hdGUgb3VyIGNvbmZpZGVuY2UgaW50ZXJ2YWwKICAgIC0gbm90IHBlcmZlY3QsIGJ1dCBzdGlsbCB1c2VmdWwKLSBJZiBib290c3RyYXAgc2FtcGxpbmcgZGlzdGlyYnV0aW9uIGhhcyBiaWcgZ2FwcyBvciBvdGhlciBwcm9ibGVtcywgCiAgICAtIHdlIHNob3VsZCB0aGluayBoYXJkIGFib3V0IGFsdGVybmF0aXZlIHNvbHV0aW9ucy4uLiAKICAgIC0gZS5nLiwgd2hhdCdzIG1ha2luZyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHNvIHVnbHk/CgoKIyMjIyBPdXIgcmVzdWx0cz8KCi0gdGhlIGRpc3RyaWJ1dGlvbiBpcyBub3QgcHJldHR5Li4uIGl0J3Mgc29ydCBvZiBhIGJvcmRlcmxpbmUgY2FzZQogICAgLSBkZWZpbml0ZWx5IG5vdCBOb3JtYWwsIGJ1dCBwb3NzaWJseSBzdGlsbCB1c2VmdWwKICAgIC0gd2Ugc2hvdWxkIGV2ZW4gYmUgc3VzcGljaW91cyBvZiBhIGJvb3RzcmFwIHBlcmNlbnRpbGUgaW50ZXJ2YWwKICAgICAgICAtIHdlJ2xsIHN1cHBvc2UgaGUgd2FudHMgdG8gODAlIGNvbmZpZGVudCBoaXMgZmxpZ2h0cyB3aWxsIGFycml2ZSBvbiB0aW1lIDk3JSBvZiB0aGUgdGltZQotIERpc2N1c3Npb24gcXVlc3Rpb25zCiAgICAtIFE6IEhvdyBzaG91bGQgQ2hyaXMgZGVjaWRlIGhvdyBlYXJseSBoZSBzaG91bGQgcGxhbiB0byBhcnJpdmU/CiAgICAtIFE6IFdoeSBjb3VsZCBtYWtlIHRoaXMgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBsb29rIHNvIHN0cmFuZ2U/CgpgYGB7cn0KQm9vdFN0cmFwVHJpYWxzICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBxdWFudGlsZSkpCgojIE1heWJlIENocmlzIGp1c3Qgd2FudHMgdG8gYmUgODAlIGNvbmZpZGVudCBoZSB3aWxsIGJlIG9uIHRpbWUgOTclIG9mIHRoZSB0aW1lCnFkYXRhKH4gcXVhbnRpbGUsIHAgPSAwLjgsIGRhdGEgPSBCb290U3RyYXBUcmlhbHMpCgpgYGAKCgoKIyMgT3V0bGllcnMKCi0gUGFydCBvZiB0aGUgcHJvYmxlbSBmb3IgQ2hyaXMgd2FzIHRoZSBmYWN0IHRoYXQgCiAgICAxLiB3ZSB3ZXJlIGludGVyZXN0ZWQgaW4gY2hhcmFjdGVyaXppbmcgdGhlIHRhaWwgb2YgdGhlIGRpc3JpYnV0aW9uOyAKICAgIDIuIHdlIGhhZCBhIGxvdCBvZiBleHRyZW1lIG9ic2VydmF0aW9ucyAocG90ZW50aWFsIG91dGxpZXJzKQotIEFuICoqb3V0bGllcioqIGlzIGFuIG9ic2VydmF0aW9uIHRoYXQgZG9lc24ndCBzZWVtIHRvIGNvbmZvcm0gdG8gdGhlIHBhdHRlcm4gb2YgdGhlIGRhdGEKICAgIC0gKipjb21wbGV0ZWx5IGxlZ2l0aW1hdGUgb2JzZXJ2YXRpb25zIG1heSBhcHBlYXIgdG8gYmUgb3V0bGllcnMqKgogICAgLSBwcm9ibGVtYXRpYyBvdXRsaWVycyBhcmUgY2FzZXMgdGhhdCBhcmUgZnVuZGFtZW50YWxseSBkaWZmZXJlbnQgZnJvbSB0aGUgcG9wdWxhdGlvbgogICAgICAgIC0gUTogZXhhbXBsZXM/Ci0gV2UgY2FuIGxlYXJuIGEgbG90IGJ5IHN0dWR5aW5nIGV4dHJlbWUgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhCgojIyBMb25nIGRlbGF5cwoKLSBUaGUgbG9uZ2VzdCBkZWxheXMgKD4gNiBocnMpIHdlcmUgbW9zdCBvZnRlbiBhc3NvY2lhdGVkIHdpdGggQW1lcmljYW4gRWFnbGUgKE1RKSBhbmQgVW5pdGVkIEFpcmxpbmVzIChVQSkgYW5kIGZyZXF1ZW50bHkgb2NjdXIgaW4gdGhlIFNwcmluZyBtb250aHMgKE1hcmNoLCBBcHJpbCwgTWF5KS4gIAogICAgLSBROiBXaGF0IHNob3VsZCB3ZSBkbyB3aXRoIHRoaXMgaW5mb3JtYXRpb24/CgpgYGB7cn0KT3JkRGVsYXlzICU+JQogIGZpbHRlcihhcnJfZGVsYXkgPj0gMzYwKSAlPiUKICB0cmFuc211dGUoY2FycmllciwgbW9udGgsIGRheSwgZGVwX2RlbGF5LCBhcnJfZGVsYXksIGFpcl9kZWxheSA9IGFycl9kZWxheSAtIGRlcF9kZWxheSkgJT4lCiAgYXJyYW5nZShkZXNjKGFycl9kZWxheSkpCmBgYAoKIyMjIyAxMzIgbWludXRlIGRlbGF5cwoKLSBBY3R1YWxseSwgQW1lcmljYW4gRWFnbGUgKE1RKSBpc24ndCBzbyBiYWQgYWZ0ZXIgbG9va2luZyBhdCBjYXJyaWVyIHZvbHVtZSAmIGNvbXBhcmluZyB3aXRoIENocmlzJyB0aHJlc2hvbGQKLSBCZXN0IGNhcnJpZXIgcGVyZm9ybWFuY2UKICAgIC0gQW1lcmljYW4gQWlybGluZXMgbG9va3MgYmV0dGVyIHRoYW4gYXZlcmFnZQogICAgLSBVbml0ZWQgbWVldHMgZXhwZWN0YXRpb247IAotIEJlbG93IGF2ZXJhZ2UgY2FycmllciBwZXJmb3JtYW5jZQogICAgLSBQaW5uYWNsZSBBaXJsaW5lcyAoOUUpIAogICAgLSBKZXRCbHVlIChCNikgCgpgYGB7cn0KIyBpbnNwZWN0IGxvbmcgZGVsYXkgdm9sdW1lCk9yZERlbGF5cyAlPiUKICBmaWx0ZXIoIWlzLm5hKGFycl9kZWxheSkpICU+JQogIG11dGF0ZShsb25nX2RlbGF5ID0gYXJyX2RlbGF5ID49IDEzMikgJT4lCiAgdGFsbHkoIH4gbG9uZ19kZWxheSB8IGNhcnJpZXIsIGRhdGEgPSAuLCBmb3JtYXQgPSAiY291bnQiLCBtYXJnaW5zID0gVFJVRSkKCiMgaW5zcGVjdCBsb25nIGRlbGF5IHByb3BvcnRpb24KT3JkRGVsYXlzICU+JQogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSkgJT4lCiAgbXV0YXRlKGxvbmdfZGVsYXkgPSBhcnJfZGVsYXkgPj0gMTMyKSAlPiUKICB0YWxseSggfiBsb25nX2RlbGF5IHwgY2FycmllciwgZGF0YSA9IC4sIGZvcm1hdCA9ICJwZXJjZW50IikgJT4lCiAgcm91bmQoMikKCmBgYAoKIyMgV2UgbmVlZCBhIHdheSB0byBhY2NvdW50IGZvciBvdGhlciB2YXJpYWJsZXMgaW4gb3VyIG1vZGVsLi4uCgotIFE6IEFyZSB0aGUgZWZmZWN0cyB3ZSdyZSBvYnNlcnZpbmcgKipyZWFsKiogb3IgYXJlIHdlIGp1c3QgYmVpbmcgZm9vbGVkIGJ5ICoqcmFuZG9tbmVzcyoqPwotIFE6IEhvdyBzaG91bGQgd2UgZXZhbHVhdGU/CiAgICAtIGludGVycHJldAogICAgLSBjb25jbHVkZQogICAgLSB3aGF0IGFib3V0IG1vcmUgZGF0YT8KCmBgYHtyfQojIGZvciBzaW1wbGljaXR5LCB3ZSBtb2RlbCBhdmVyYWdlIGFycml2YWwgZGVsYXlzIGhlcmUKb3JkTW9kZWwgPC0gCiAgT3JkQ29sbGVhZ3VlRGF0YSAlPiUgCiAgbXV0YXRlKGNhcnJpZXJBQSA9IGlmX2Vsc2UoY2FycmllciAlaW4lIGMoIkFBIiksIHRydWUgPSAiQUEiLCBmYWxzZSA9ICJPdGhlckNhcnJpZXIiKSwgCiAgICAgICAgIHNlYXNvbiA9IGlmX2Vsc2UobW9udGggJWluJSAzOjUsIHRydWUgPSAiU3ByaW5nIiwgZmFsc2UgPSAiTm90U3ByaW5nIikpICU+JQogIGxtKGFycl9kZWxheSB+IGNhcnJpZXJBQSArIHNlYXNvbiwgZGF0YSA9IC4pCgojIEhvdyB0byBpbnRlcnByZXQ/IFdoYXQgaGF2ZSB3ZSBsZWFybmVkPyAKbXN1bW1hcnkob3JkTW9kZWwpICAKY29uZmludChvcmRNb2RlbCkgIApgYGAKCjwhLS0gRGF5MyAtLT4KCgojIyBTZWd1ZSB0byBNb2RlbCBCYXNpY3MKCi0gV2Ugd2VyZSB0cnlpbmcgdG8gdXNlIENocmlzJyBmbGlnaHQgZGF0YSB0byBjb21lIHVwIHdpdGggYSBydWxlIHRoYXQgY291bGQgYmUgdXNlZnVsIGZvciBwcmV2ZW50aW5nIGV4Y2Vzc2l2ZSBkZWxheXMgZnJvbSBqZW9wYXJkaXppbmcgaGlzIG1lZXRpbmdzIGluIENoaWNhZ28KICAgIC0gV2Ugc3RhcnRlZCBieSBvbmx5IGxvb2tpbmcgYXQgdGhlIGFycml2YWwgZGVsYXkgaXRzZWxmIGBhcnJfZGVsYXlgCiAgICAtIEFmdGVyIHBva2luZyBhcm91bmQgZm9yIGEgd2hpbGUsIHdlIG5vdGljZWQgdGhhdCB1c2VmdWwgaW5mb3JtYXRpb24gbWlnaHQgYmUgZ2FpbmVkIGZyb20gb3RoZXIgdmFyaWFibGVzCiAgICAgICAgLSBjYXJyaWVyCiAgICAgICAgLSB0aW1lIG9mIHllYXIgKGxvbmdlc3QgZGVsYXlzIGluIFNwcmluZykKCgojIyBNdWx0aXBsZSBUZXN0aW5nCgotIElmIHlvdSBlbmQgdXAgZXZhbHVhdGluZyBtdWx0aXBsZSB0ZXN0cyBvZiB0aGUgc2FtZSBkYXRhLCB5b3UgbG9zZSBjb250cm9sIG9mIFR5cGUgSSBlcnJvciByYXRlCiAgICAtIFE6IFdoYXQgZG9lcyB0eXBlIEkgZXJyb3IgcmF0ZSBtZWFuPwogICAgLSBROiBIb3cgZG8gd2UgcXVhbnRpZnkgdHlwZSBJIGVycm9yIHJhdGU/CiAgICAtIFE6IFdoYXQncyB0aGUgYmlnIGRlYWw/ICBXaHkgZG9lcyBpdCBtYXR0ZXI/PwoKLSBNdWx0aXBsZSB0ZXN0aW5nIGFuZCBhcHByb3ByaWF0ZSBhZGp1c3RtZW50cwoKYGBge3J9CiMgMyBUZXN0cy4uLgoxIC0gKDEgLSAwLjA1KV4zCgojIEJvbmZlcnJvbmkgYWRqdXN0bWVudCBmb3IgMyB0ZXN0cwpiIDwtIDAuMDUgLyAzCjEgLSAoMSAtIGIpXjMKCmBgYAoKIyMgQVNBIFN0YXRlbWVudCBvbiBwLXZhbHVlcy4uLgoKLSBMb3RzIG9mIHBlb3BsZSBoYXZlIGhlYXJkIG9mICJwLXZhbHVlcyIgYW5kIGhhdmUgc29tZSB2YWd1ZSBjb25jZXB0IG9mICJzbWFsbGVyIHRoZSBiZXR0ZXIiCi0gSW5jb21wbGV0ZSB1bmRlcnN0YW5kaW5nIGNhbiBsZWFkIHRvIGJvZ3VzIGNvbmNsdXNpb25zIGFuZCBkaXN0cnVzdCBvZiBzdGF0aXN0aWNpYW5zICYgZGF0YSBhbmFseXN0cwotIEhlcmUncyB3aGF0IHRoZSBBbWVyaWNhbiBTdGF0aXN0aWNhbCBBc3NvYyB3YW50cyBldmVyeW9uZSB0byBrbm93IGFib3V0IHAtdmFsdWVzCiAgICAxLiBwLXZhbHVlcyBjYW4gaW5kaWNhdGUgaG93ICoqaW4qKmNvbXBhdGlibGUgdGhlIGRhdGEgYXJlIHdpdGggYSBzcGVjaWZpZWQgbW9kZWwKICAgIDIuIHAtdmFsdWVzIGRvIE5PVCBtZWFzdXJlIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBzdHVkaWVkIGh5cG90aGVzaXMgaXMgdHJ1ZSwgb3IgdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIGRhdGEgd2VyZSBwcm9kdWNlZCBieSByYW5kb20gY2hhbmNlIGFsb25lCiAgICAzLiBzY2llbnRpZmljIGNvbmNsdXNpb25zIGFuZCBidXNpbmVzcyBvciBwb2xpY3kgZGVjaXNpb25zIHNob3VsZCBOT1QgYmUgYmFzZWQgb25seSBvbiB3aGV0aGVyIGEgcC12YWx1ZSBwYXNzZXMgYSBzcGVjaWZpYyB0aHJlc2hvbGQKICAgIDQuIHByb3BlciBpbmZlcmVuY2UgcmVxdWlyZXMgZnVsbCByZXBvcnRpbmcgYW5kIHRyYW5zcGFyZW5jeQogICAgNS4gYSBwLXZhbHVlLCBvciBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UsIGRvZXMgbm90IG1lYXN1cmUgdGhlIHNpemUgb2YgYW4gZWZmZWN0IG9yIHRoZSBpbXBvcnRhbmNlIG9mIGEgcmVzdWx0CiAgICA2LiBieSBpdHNlbGYsIGEgcC12YWx1ZSBkb2VzIG5vdCBwcm92aWRlIGEgZ29vZCBtZWFzdXJlIG9mIGV2aWRlbmNlIHJlZ2FyZGluZyBhIG1vZGVsIGh5cG90aGVzaXMuCgoKIyMgSHlwb3RoZXNpcyBnZW5lcmF0aW9uIHZzLiBoeXBvdGhlc2lzIGNvbmZpcm1hdGlvbiAKCi0gdHJhZGl0aW9uYWwgZm9jdXMgb2YgbW9kZWxsaW5nIGlzIG9uIGluZmVyZW5jZS0tYXR0ZW1wdGluZyB0byBjb25maXJtIHNvbWUgaHlwb3RoZXNpcyBpcyB0cnVlIChvciBub3QgZmFsc2UuLi4pCi0gdGhlIHByb2Nlc3MgaXMgbm90ICpjb21wbGljYXRlZCosIGJ1dCBjYW4gYmUgKmhhcmQqIGJlY2F1c2Ugd2UgbmVlZCB0byB1bmRlcnN0YW5kIHRoYXQKICAgIDEuIEVhY2ggb2JzZXJ2YXRpb24gY2FuIGVpdGhlciBiZSB1c2VkIGZvciBleHBsb3JhdGlvbiBvciBjb25maXJtYXRpb24sIG5vdCBib3RoLgogICAgMi4gWW91IGNhbiB1c2UgYW4gb2JzZXJ2YXRpb24gYXMgbWFueSB0aW1lcyBhcyB5b3UgbGlrZSBmb3IgZXhwbG9yYXRpb24sIGJ1dCBvbmx5IG9uY2UgZm9yIGNvbmZpcm1hdGlvbi4gCi0gKkFzIHNvb24gYXMgeW91IHVzZSBhbiBvYnNlcnZhdGlvbiB0d2ljZSwgeW914oCZdmUgc3dpdGNoZWQgZnJvbSBjb25maXJtYXRpb24gdG8gZXhwbG9yYXRpb24uKiAgSWYgcG9zc2libGUsIGNvbnNpZGVyIHBhcnRpdGlvbmluZyB0aGUgZGF0YSBhcyBmb2xsb3dzOgogICAgLSA2MCUgb2YgdGhlIGRhdGEgZm9yICoqZXhwbG9yYXRpb24qKiAob3IgdHJhaW5pbmcpIHNldAogICAgLSAyMCUgb2YgdGhlIGRhdGEgZm9yIGEgKipxdWVyeSoqIChvciB2YWxpZGF0aW9uKSBzZXQKICAgIC0gMjAlIG9mIHRoZSBkYXRhIGZvciBhICoqdGVzdCoqIChvciBjb25maXJtYXRpb24pIHNldAoKCgojIyBNb2RlbCBCYXNpY3MKCiMjIyMgR29hbHMgb2YgbW9kZWxpbmc6CgotICJwcm92aWRlIGEgc2ltcGxlLCBsb3ctZGltZW5zaW9uYWwgc3VtbWFyeSBvZiBhIGRhdGEgc2V0IiAoUjREUykKLSBZb3UgbWF5IHVzZSB0aGVzZSB0b29scyBpbiBkaWZmZXJlbnQgY29udGV4dHMgZm9yIHZhcmlvdXMgcHVycG9zZXMKICAgIC0gZGVzY3JpcHRpb24KICAgIC0gaW5mZXJlbmNlCiAgICAtIHByZWRpY3Rpb24KLSBJbiBlYWNoIGNhc2UsIHdlIGFyZSAqZXhwbGFpbmluZyB2YXJpYXRpb24qCiAgICAtIHNvbWUgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGlzIHN0cnVjdHVyYWwKICAgIC0gc29tZSB2YXJpYXRpb24gaW4gdGhlIGRhdGEgaXMgc2ltcGx5IHJhbmRvbW5lc3MKCiMjIENob29zaW5nIGEgbW9kZWwKCi0gTW9kZWwgZmFtaWx5IChnZW5lcmFsKQogICAgLSBDaG9zZW4gYmFzZWQgb24gdW5kZXJzdGFuZGluZyBvZiBkYXRhIGFuZCBnb2FscyBvZiBhbmFseXNpcyAKICAgIC0gRXhwcmVzc2VzIGEgcHJlY2lzZSAoYnV0IGdlbmVyaWMpIHBhdHRlcm4geW91IHdhbnQgdG8gY2FwdHVyZQogICAgICAgIC0gTGluZTogJHkgPSBhXzEgKyBhXzIgKiB4JAogICAgICAgIC0gQ3VydmU6ICR5ID0gYV8xICogeF57YV8yfSQKICAgICAgICAtICR5JCAmICR4JCBhcmUgKip2YXJpYWJsZXMqKiB0aGF0IGFyZSAqKm9ic2VydmVkKiogaW4geW91ciBkYXRhCiAgICAgICAgLSAkYV8xJCAmICRhXzIkIGFyZSAqKnBhcmFtZXRlcnMqKiB0aGF0IGNhbiBiZSBtb2RpZmllZCB0byBjYXB0dXJlIGRpZmZlcmVudCBwYXR0ZXJucwotIEZpdHRlZCBtb2RlbCAoc3BlY2lmaWMpCiAgICAtICoqQ29uc3RyYWluZWQgYnkgbW9kZWwgZmFtaWx5KioKICAgIC0gQ2hvb3NlIHBhcmFtZXRlcnMgdGhhdCByZXN1bHQgaW4gdGhlIG1vZGVsIHRoYXQgaXMgImNsb3Nlc3QiIHRvIHlvdXIgZGF0YQogICAgICAgIC0gTGluZTogJHkgPSA3ICsgMyAqIHgkIAogICAgICAgIC0gQ3VydmU6ICR5ID0gOSAqIHheMiQgCgojIyBBbGwgbW9kZWxzIGFyZSB3cm9uZyAoc29tZSBhcmUgdXNlZnVsKQotICJBbGwgbW9kZWxzIGFyZSB3cm9uZywgc29tZSBhcmUgdXNlZnVsIiAtLUdlb3JnZSBCb3ggKHN0YXRpc3RpY2lhbikKLSBDbGFzc2ljYWwgTWVjaGFuaWNzIChwaHlzaWNzKTogCiAgICAtIElzYWFjIE5ld3RvbidzICJsYXdzIiBvZiBtb3Rpb24gKHBoeXNpY3MpIGFyZSBkZW1vbnN0cmFibHkgKip3cm9uZyoqCiAgICAgICAgLSBUaGUgbW9kZWwgZ2VuZXJhbGx5IGRvZXMgd2VsbCBkZXNjcmliaW5nIG1vdGlvbiBmb3Igb2JqZWN0cyBhcyBzbWFsbCBhcyBiYWN0ZXJpYSBhbmQgYXMgbGFyZ2UgYXMgcGxhbmV0cywgc3RhcnMsIGFuZCBnYWxheGllcy4gIAogICAgICAgIC0gTmV3dG9uJ3MgbGF3cyB3ZXJlIGtub3duIHRvIGJlIGluYWNjdXJhdGUgd2hlbiBwcmVkaWN0aW5nIHRoZSBvcmJpdCBvZiBNZXJjdXJ5CiAgICAtIENsb3NlIGVub3VnaD8hIChpLmUuICoqdXNlZnVsKio/KQotIEVpbnN0ZWluJ3MgdGhlb3J5IG9mIChnZW5lcmFsKSByZWxhdGl2aXR5CiAgICAtIENsYXNzaWNhbCBtZWNoYW5pY3MgYnJlYWtzIGRvd24gd2hlbiBib2RpZXMgaW52b2x2ZWQgYXJlIGV4dHJlbWVseSBtYXNzaXZlIG9yIG1vdmluZyBleHRyZW1lbHkgZmFzdAogICAgLSBBY2N1cmF0ZSBHUFMgcmVxdWlyZXMgcmVsYXRpdml0eSAoTmV3dG9uIHdvdWxkIGJlIG9mZiBieSBhIGNvdXBsZSBtaWxlcykKCiMjIyMgTW9yYWw6IGFsbCBtb2RlbHMgYXJlIHdyb25nOyBzb21lIGFyZSB1c2VmdWwKCjEuIE5ld3RvbmlhbiBwaHlzaWNzIHdhcyBrbm93biB0byBiZSAqKndyb25nKiogZm9yIG5lYXJseSAyIGNlbnR1cmllcyBiZWZvcmUgcmVsYXRpdml0eSBjYW1lIGFsb25nLCBpbiBwYXJ0IGJlY2F1c2UgaXQgZmFpbGVkIHRvIGFjY3VyYXRlbHkgcHJlZGljdCB0aGUgb3JiaXQgb2YgTWVyY3VyeS4gIFRoZSBtb2RlbCB3YXMgKGFuZCBpcz8pICoqdXNlZnVsKiogZm9yIHRvbnMgb2YgcHJhY3RpY2FsIHB1cnBvc2VzCgoyLiBFaW5zdGVpbidzIHRoZW9yeSBvZiByZWxhdGl2aXR5IGlzIGFsbW9zdCBjZXJ0YWlubHkgKip3cm9uZyoqIHRvbyBiZWNhdXNlIGl0IGZhaWxzIGZvciBzdWJhdG9taWMgcGFydGljbGVzLi4uIGJ1dCBpdCdzIHN0aWxsICoqdXNlZnVsKiogCgoKIyMgU2ltcGxlIGV4YW1wbGUKCi0gV2UnbGwgdXNlIGxpbmVhciByZWdyZXNzaW9uIGFzIGEgc2FuZGJveCB0byBidWlsZCBzb21lIGludHVpdGlvbiBhbmQgdG9vbHMKICAgIC0gb25jZSB5b3UgdW5kZXJzdGFuZCBzb21lIGJhc2ljIHByaW5jaXBsZXMgaW4gdGhlIGNvbnRleHQgb2YgcmVncmVzc2lvbiwgd2UgY2FuIHRyYW5zbGF0ZSB0byBvdGhlciBtb2RlbHMKLSBjb25zaWRlciBhIHNpbXVsYXRlZCBkYXRhIHNldCBgc2ltMWAgd2l0aCB2YXJpYWJsZXMgYHlgIGFuZCBgeGAKLSBpdCBsb29rcyBsaWtlIHRoZXkncmUgcmVsYXRlZCB0byBvbmUgYW5vdGhlciwgc28gcGVyaGFwcyB3ZSBjYW4gbW9kZWwgdGhlaXIgcmVsYXRpb25zaXAKICAgIC0gRmFtaWx5OiAkeSA9IGJfMCArIGJfMSAqIHgkCiAgICAtIEZpdDogY2hvb3NlIHRoZSAiYmVzdCIgJGJfMCQgYW5kICRiXzEkCgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgU2ltcGxlIGV4YW1wbGUgKHJhbmRvbSBtb2RlbHMpCgotIHdlIGNvdWxkIGp1c3QgcmFuZG9tbHkgZ2VuZXJhdGUgbW9kZWxzIGFuZCBwaWNrIGEgZmV3IGNhbmRpZGF0ZXMgdGhhdCBsb29rICJiZXN0IgotIGludHVpdGlvbiBzYXlzIHRoaXMgaXMgc2lsbHkgYXBwcm9hY2guLi4gCiAgICAtIFdoeSBkbyB3ZSB0aGluayBrbm93IGJldHRlcj8KICAgIC0gSG93IG1pZ2h0IHdlIGp1ZGdlIHdoZXRoZXIgb25lIG1vZGVsIGlzICJiZXR0ZXIiIHRoYW4gYW5vdGhlcj8KCgpgYGB7cn0KbW9kZWxzIDwtIAogIHRpYmJsZSgKICBiMCA9IHJ1bmlmKDI1MCwgLTIwLCA0MCksCiAgYjEgPSBydW5pZigyNTAsIC01LCA1KQogICkKCnNpbTEgJT4lIAogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxKSwgZGF0YSA9IG1vZGVscywgYWxwaGEgPSAwLjI1KSArIAogIGdlb21fcG9pbnQoKSAKYGBgCgoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gdGhlIG1vZGVsICgxLzQpCgotIGEgbmF0dXJhbCB0aG91Z2h0IGlzIHRvIGV2YWx1YXRlIHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBmcm9tIGVhY2ggcG9pbnQgdG8gYSBjYW5kaWRhdGUgbW9kZWwKICAgIC0gdGhlIHBvaW50IHJlcHJlc2VudHMgb3VyIG9ic2VydmVkICoqcmVzcG9uc2UqKiAoZnJvbSB0aGUgZGF0YSkKICAgIC0gdGhlIGNvcnJlc3BvbmRpbmcgbG9jYXRpb24gb24gdGhlIG1vZGVsIGlzIGEgKipwcmVkaWN0aW9uKioKLSBtb2RlbCBmaXR0aW5nIGFwcGxldDogPGh0dHA6Ly93d3cucm9zc21hbmNoYW5jZS5jb20vYXBwbGV0cy9SZWdTaHVmZmxlLmh0bT4KLSBOb3RlOiB4LXZhbHVlcyBhcmUgc2hpZnRlZCBzbGlnaHRseSB0byBhdm9pZCBvdmVycGxvdHRpbmcgaW4gdGhlIGZpZ3VyZQoKIVtdKGFic0Vycm9yc1I0RFMucG5nKQoKCiMjIERpc3RhbmNlIGZyb20gcG9pbnRzIHRvIHRoZSBtb2RlbCAoMi80KQoKLSBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2UgZGlyZWN0bHkKICAgIC0gbGV0J3MgdHVybiBvdXIgbW9kZWwgaW50byBhIGZ1bmN0aW9uCgpgYGB7cn0KbW9kZWwxIDwtIGZ1bmN0aW9uKGEsIGRhdGEpIHsKICAjIHB1cnBvc2U6IGNhbGN1bGF0ZSBwcmVkaWN0aW9ucyBmb3IgYSBnaXZlbiBtb2RlbCAKICAjIGlucHV0czogCiAgIyMjIGE6IHZlY3RvciBvZiBjb2VmZmljaWVudCBlc3RpbWF0ZXMgZm9yIGxpbmVhciBtb2RlbCAoZS5nLiBpbnRlcmNlcHQgJiBzbG9wZXMpCiAgIyMjIGRhdGE6IHZlY3RvciBvZiBvYnNlcnZlZCByZXNwb25zZSBkYXRhCiAgCiAgYVsxXSArIGRhdGEkeCAqIGFbMl0KfQoKIyBzaG93IG1vZGVsIHByZWRpY3Rpb25zCm1vZGVsMShjKDcsIDEuNSksIHNpbTEpCmBgYAoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gdGhlIG1vZGVsICgzLzQpCgotIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZSBkaXJlY3RseQogICAgLSBsZXQncyB0dXJuIG91ciBtb2RlbCBpbnRvIGEgZnVuY3Rpb24KICAgIC0gbWVhc3VyZSBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gKiphbGwgcG9pbnRzKiogdG8gdGhlIG1vZGVsCiAgICAgICAgLSBTdGF0aXN0aWNpYW5zIHR5cGljYWxseSB1c2UgdGhlIOKAnHJvb3QtbWVhbi1zcXVhcmVkIiBkZXZpYXRpb24KCmBgYHtyfQptZWFzdXJlX2Rpc3RhbmNlIDwtIGZ1bmN0aW9uKG1vZCwgZGF0YSkgewogICMgcHVycG9zZTogY2FsY3VsYXRlIGRpc3RhbmNlIGZyb20gb2JzZXJ2ZWQgcG9pbnRzIHRvIG1vZGVsIHByZWRpY3Rpb25zCiAgIyBpbnB1dHM6IAogICMjIyBtb2Q6IHZlY3RvciBvZiBwYXJhbWV0ZXIgZXN0aW1hdGVzIGZvciBsaW5lYXIgbW9kZWwgKGUuZy4gaW50ZXJjZXB0ICYgc2xvcGUocykpCiAgIyMjIGRhdGE6IG9ic2VydmVkIGRhdGEgCiAgCiAgZGlmZiA8LSBkYXRhJHkgLSBtb2RlbDEobW9kLCBkYXRhKQogIHNxcnQobWVhbihkaWZmIF4gMikpCn0KCiMgc2hvdyBSTVMgZGV2aWF0aW9uIGZvciB0aGUgbW9kZWwKbWVhc3VyZV9kaXN0YW5jZShjKDcsIDEuNSksIHNpbTEpCmBgYAoKIyMgRGlzdGFuY2UgZnJvbSBwb2ludHMgdG8gZWFjaCBjYW5kaWRhdGUgbW9kZWxzICg0LzQpCgotIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZSBkaXJlY3RseQogICAgLSBsZXQncyB0dXJuIG91ciBtb2RlbCBpbnRvIGEgZnVuY3Rpb24KICAgIC0gbWVhc3VyZSBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gKiphbGwgcG9pbnRzKiogdG8gdGhlIG1vZGVsCi0gcmVwZWF0IGZvciBhbGwgMjUwIG9mIG91ciBtb2RlbHMgdXNpbmcgYHB1cnJyOjptYXAyKClgIAoKYGBge3J9CnNpbTFfZGlzdCA8LSBmdW5jdGlvbihiMCwgYjEpIHsKICAjIHB1cnBvc2U6IGhlbHBlciBmdW5jdGlvbiBiZWNhdXNlIG91ciBkaXN0YW5jZSBmdW5jdGlvbiAKICAjICAgICAgICAgICAgZXhwZWN0cyB0aGUgbW9kZWwgYXMgYSBudW1lcmljIHZlY3RvciBvZiBsZW5ndGggMgogICMgaW5wdXRzOiAKICAjIyMgYjA6IGludGVyY2VwdCBlc3RpbWF0ZQogICMjIyBiMTogc2xvcGUgZXN0aW1hdGUgCiAgIyMjIHNpbTE6IGRhdGEgaW4gZW52aXJvbm1lbnQKCiAgbWVhc3VyZV9kaXN0YW5jZShjKGIwLCBiMSksIHNpbTEpCn0KCm1vZGVscyA8LSAKICBtb2RlbHMgJT4lIAogIG11dGF0ZShkaXN0ID0gcHVycnI6Om1hcDJfZGJsKGIwLCBiMSwgLmYgPSBzaW0xX2Rpc3QpKQoKbW9kZWxzCmBgYAoKIyMjIyBQcm9ncmVzcy4uLgoKLSBXZSBub3cgaGF2ZSBhIGRpc3RhbmNlIG1ldHJpYyB0aGF0IHF1YW50aWZpZXMgcGVyZm9ybWFuY2Ugb2YgYWxsIDI1MCBvZiBvdXIgcmFuZG9tIG1vZGVscyAKLSBOb3cgd2UgY2FuIHN0YXJ0IHRvIGV2YWx1YXRlIGlmIHNvbWUgb2YgdGhlbSBhcmUgKip1c2VmdWwqKgoKYGBge3J9CnNpbTEgJT4lIAogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxKSwgZGF0YSA9IG1vZGVscywgYWxwaGEgPSAwLjI1KSArIAogIGdlb21fcG9pbnQoKSAKYGBgCgoKCiMjIEJlc3QgKHJhbmRvbSkgbW9kZWxzIGZvciBgc2ltMWAgZGF0YQoKLSB3ZSBoYXZlIGEgbWV0cmljCi0gbGV0J3MgaW5zcGVjdCBvdXIgdG9wIDEwIHJlc3VsdHMgYWNjb3JkaW5nIHRvIG91ciBkaXN0YW5jZSBjcml0ZXJpb24KICAgIC0gYnJpZ2h0ZXN0IG1vZGVscyBmaXQgdGhlIGRhdGEgImJlc3QiCiAgICAtIG5vdGUgYWxsIHRoZSBvYnZpb3VzbHkgc2lsbHkgbW9kZWxzIGFyZSBnb25lCiAgICAtIHNldmVyYWwgZGVjZW50IGNhbmRpZGF0ZSBtb2RlbHMgbGVmdAotIFE6IEhvdyBjYW4gd2UgZG8gKmV2ZW4gYmV0dGVyKj8KCgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHkpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGNvbG91ciA9ICJncmV5MzAiKSArIAogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBiMCwgc2xvcGUgPSBiMSwgY29sb3VyID0gLWRpc3QpLCAKICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKG1vZGVscywgcmFuayhkaXN0KSA8PSAxMCkpCmBgYAoKPCEtLSBCZWdpbiBEYXkgNCAoMS8zMCkgLS0+CgojIyBCZXN0IChyYW5kb20pIG1vZGVscyBmb3IgYHNpbTFgIGRhdGEKCi0gV2hhdCBpZiB3ZSB2aXN1YWxpemVkIHRoZSBzbG9wZSAoYjEpLCBpbnRlcmNlcHQgKGIwKSwgYW5kIGRpc3RhbmNlIG1ldHJpYyAoZGlmZikgb24gYSAzLWRpbWVuc2lvbmFsIHBsb3Q/CiAgICAtIHNsb3BlIGFuZCBpbnRlcmNlcHQgYXJlICoqYm90aCoqIHJlbGF0ZWQgdG8gZGlzdGFuY2UgbWV0cmljCiAgICAtIHdlIGp1c3Qgd2FudCB0byBzZWFyY2ggZm9yIHRoZSAiYmVzdCIgY29tYmluYXRpb25zIHVzaW5nIG91ciBjcml0ZXJpb24KLSBwb2ludHMgYXJlIHNsb3BlL2ludGVyY2VwdCBwYWlycyAoaS5lLiBtb2RlbHMpIGNvbG9yZWQgYnkgb3VyIGRpc3RhbmNlIGNyaXRlcmlvbiAKICAgIC0gYnJpZ2h0ZXIgY29sb3IgaXMgImJldHRlciIgKGkuZS4gbGVzcyBjb2xsZWN0aXZlIGRpc3RhbmNlIGZyb20gbW9kZWwgdG8gZGF0YSkKICAgIC0gdGhlIHRvcCAxMCBzaG93biBwcmV2aW91c2x5IGFyZSBoaWdobGlnaHRlZCBpbiByZWQKCmBgYHtyfQpnZ3Bsb3QobW9kZWxzLCBhZXMoYjAsIGIxKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihtb2RlbHMsIHJhbmsoZGlzdCkgPD0gMTApLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSAtZGlzdCkpCmBgYAoKIyMgUmFuZG9tIG1vZGVscyB3YXMgc2lsbHkuLi4gYnV0IG5vdCAqdGhhdCogc2lsbHkKCi0gVHVybnMgb3V0IHdlIGNvdWxkIGxlYXJuIHF1aXRlIGEgYml0IHdpdGggdGhpcyBtZXRob2QsIGFuZCBjYW1lIHVwIHdpdGggYSBoYW5kZnVsIG9mIGRlY2VudCBtb2RlbHMKLSBROiBIb3cgbWlnaHQgd2UgZXh0ZW5kIHRoZSBtZXRob2QgZm9yIGEgbW9yZSBzeXN0ZW1hdGljIGFwcHJvYWNoPwoKCgoKIyMgU3lzdGVtYXRpYyBzZWFyY2gKCi0gTWF5YmUgaW5zdGVhZCBvZiBzZWFyY2hpbmcgcmFuZG9tbHksIHdlIHNlYXJjaCAqc3lzdGVtYXRpY2FsbHkqIHdpdGggYSBncmlkIG9mIHNsb3BlLWludGVyY2VwdCBjb21iaW5hdGlvbnM/CiAgICAtIHdlJ2xsIHN0YXJ0IHdpdGggMjUwIG1vZGVscyBhZ2FpbgogICAgLSB3ZSBjYW4gY29udHJvbCBob3cgZmluZSB0aGUgZ3JpZCBpcyBpZiB3ZSB3YW50CiAgICAtIGF2b2lkIGNsdW1waW5nIGFuZCBzcGFyc2l0eSBvZiByYW5kb21uZXNzCi0gc28gZmFyIHRoZSBuZXcgZ3JpZCBzdWdnZXN0cwogICAgLSBiMCBpcyBtYXliZSBhcm91bmQgNAogICAgLSBiMSBpcyBtYXliZSBhcm91bmQgMgogICAgLSBROiBJcyB0aGUgbW9kZWwgJHkgPSA0ICsgMlgkIHdyb25nPyBJcyBpdCB1c2VmdWw/ICAKCmBgYHtyfQojIGNyZWF0ZSBzeXN0ZW1hdGljIGdyaWQ7IGZpdCBhbGwgbW9kZWxzIHRvIHRoZSBkYXRhCmdyaWQgPC0gCiAgZXhwYW5kLmdyaWQoYjAgPSBzZXEoLTUsIDIwLCBsZW5ndGggPSAyNSksIAogICAgICAgICAgICAgIGIxID0gc2VxKDEsIDMsIGxlbmd0aCA9IDI1KSkgJT4lIAogIG11dGF0ZShkaXN0ID0gcHVycnI6Om1hcDJfZGJsKGIwLCBiMSwgc2ltMV9kaXN0KSkKCiMgcGxvdCBtb2RlbHMgYXMgcG9pbnRzOyBjb2xvciAiYmVzdCIKZ3JpZCAlPiUgCiAgZ2dwbG90KGFlcyhiMCwgYjEpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKGdyaWQsIHJhbmsoZGlzdCkgPD0gMTApLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSAtZGlzdCkpIAoKc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHkpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGNvbG91ciA9ICJncmV5MzAiKSArIAogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBiMCwgc2xvcGUgPSBiMSwgY29sb3VyID0gLWRpc3QpLCAKICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKGdyaWQsIHJhbmsoZGlzdCkgPD0gMTApKQpgYGAKCgojIyBTeXN0ZW1hdGljIHNlYXJjaCBjb250J2QKCi0gbGV0J3MgdHJ5IGEgZmluZXIgZ3JpZC4uLgotIGJlc3QgcmVzdWx0cyBzbyBmYXIgCiAgICAtIGIwIGJldHdlZW4gMCBhbmQgNzsgCiAgICAtIGIxIGJldHdlZW4gMS41IGFuZCAyLjUKLSBROiBjb3VsZCB3ZSBqdXN0IGNvbnRpbnVlIHRoaXMgd2F5PwoKCmBgYHtyfQojIGNyZWF0ZSBzeXN0ZW1hdGljIGdyaWQ7IGZpdCBhbGwgbW9kZWxzIHRvIHRoZSBkYXRhCmdyaWQgPC0gCiAgZXhwYW5kLmdyaWQoYjAgPSBzZXEoMCwgNywgbGVuZ3RoID0gNTApLCAKICAgICAgICAgICAgICBiMSA9IHNlcSgxLjUsIDIuNSwgbGVuZ3RoID0gNTApKSAlPiUgCiAgbXV0YXRlKGRpc3QgPSBwdXJycjo6bWFwMl9kYmwoYjAsIGIxLCBzaW0xX2Rpc3QpKQoKIyBwbG90IG1vZGVscyBhcyBwb2ludHM7IGNvbG9yICJiZXN0IgpncmlkICU+JSAKICBnZ3Bsb3QoYWVzKGIwLCBiMSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIoZ3JpZCwgcmFuayhkaXN0KSA8PSAxMCksIHNpemUgPSA0LCBjb2xvdXIgPSAicmVkIikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IC1kaXN0KSkgCgpzaW0xICU+JQogIGdncGxvdChhZXMoeCwgeSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3VyID0gImdyZXkzMCIpICsgCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGIwLCBzbG9wZSA9IGIxLCBjb2xvdXIgPSAtZGlzdCksIAogICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoZ3JpZCwgcmFuayhkaXN0KSA8PSAxMCkpCmBgYAoKIyMgUmVjYXAgb2Ygb3VyIGdyaWQgc2VhcmNoLi4uCgotIFdlIGRlZmluZWQgYSBkaXN0YW5jZSBtZXRyaWMgKHJvb3QtbWVhbi1zcXVhcmUgZGV2aWF0aW9uKSB0aGF0IHdlIHdhbnQgdG8gdXNlIHRvIGNvbXBhcmUgbW9kZWxzCiAgICAtIFNtYWxsZXIgUk1TIGRldmlhdGlvbiBpbmRpY2F0ZXMgdGhlIG1vZGVsIGlzICJjbG9zZSIgdG8gdGhlIGRhdGEgCiAgICAtIFRoZSAiYmVzdCIgbW9kZWwgaXMgdGhlIG9uZSB0aGF0IG1pbmltaXplcyBvdXIgY3JpdGVyaWFuIChpLmUuIFJNUyBkZXZpYXRpb24pCi0gU2VhcmNoIHByb2Nlc3M6CiAgICAxLiBkZWNpZGVkIG9uIGEgbW9kZWwgZmFtaWx5IChsaW5lYXIgaW4gdGhpcyBjYXNlKQogICAgMi4gV2Ugc3RhcnRlZCB3aXRoIGEgYnVuY2ggb2YgcmFuZG9tIGxpbmVzLCBidXQgdGhlbiBkZWNpZGVkIGEgc3lzdGVtYXRpYyBncmlkIG9mIGNvbWJpbmF0aW9ucyB3YXMgYSBiZXR0ZXIgaWRlYQogICAgMy4gV2UgcGlja2VkIHRoZSAxMCBiZXN0IG1vZGVscyB1c2luZyBvdXIgY3JpdGVyaW9uCiAgICA0LiBXZSB0aGVuIHNlYXJjaGVkIGFnYWluIHdpdGggYSBmaW5lciBncmlkCiAgICA1LiBSZXBlYXQgc3RlcHMgNCAmIDUgdW50aWwgc2F0aXNmaWVkCiAgICA2LiBFc3RpbWF0ZSBtb2RlbCBwYXJhbWV0ZXJzCgoKCiMjIGBvcHRpbSgpYCBhbmQgdGhlIE5ld3Rvbi1SYXBoc29uIGFscm9naXRobQoKLSBUaGUgTmV3dG9uLVJhcGhzb24gYWxnb3JpdGhtIGlzIGEgbnVtZXJpY2FsIG9wdGltaXphdGlvbiBzZWFyY2gKICAgIDEuIHBpY2sgYSBzdGFydGluZyBwb2ludCBmb3IgeW91ciBwYXJhbWV0ZXIgZXN0aW1hdGVzCiAgICAyLiBsb29rIGFyb3VuZCBmb3Igc3RlZXBlc3Qgc2xvcGUgKGkuZS4gZ3JlYXRlc3QgaW1wcm92ZW1lbnQgYWdhaW5zdCBvdXIgbWV0cmljKQogICAgMy4gc2tpIGRvd24gdGhhdCBzbG9wZSBhIGJpdAogICAgNC4gcmVwZWF0CiAgICA1LiB5b3UncmUgZG9uZSB3aGVuIHlvdSBjYW4ndCBnZXQgYW55IGxvd2VyCi0gdGhlIGBvcHRpbSgpYCBmdW5jdGlvbiBkb2VzIHNvbWV0aGluZyBjb25jZXB0dWFsbHkgc2ltaWxhciBmb3IgdXMKICAgIC0gd2UgcHJvdmlkZSBzdGFydGluZyBwb2ludDogYGMoMCwgMClgCiAgICAtIHdlIHByb3ZpZGUgZnVuY3Rpb24gdG8gZGVmaW5lIGRpc3RhbmNlIGJldHdlZW4gbW9kZWwgYW5kIGRhdGE6IGBtZWFzdXJlX2Rpc3RhbmNlYAogICAgLSB3ZSBwcm92aWRlIGRhdGE6IGBzaW0xYCAKICAgIC0gUiBkb2VzIHRoZSByZXN0IChub3RlOiBvdXIgZ3JpZCBzZWFyY2ggd2Fzbid0IHNvIGJhZCEhKQotIFE6IGhvdyBtaWdodCB0aGlzIGFsZ29yaXRobSBwb3NzaWJseSByZXN1bHQgaW4gYSBtaXNsZWFkaW5nIGNvbmNsdXNpb24/IAogICAgLSBUaGlzIGRvZXNuJ3QgdXN1YWxseSBoYXBwZW4sIGJ1dCB5b3UgbmVlZCB0byBrbm93IGl0J3MgYSByaXNrCiAgICAtIFE6IElkZWFzIHRvIHByb3RlY3QgYWdhaW5zdCB0aGlzIHJpc2s/CgpgYGB7cn0KbnJCZXN0IDwtIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgZGF0YSA9IHNpbTEpCgojIHNob3cgcGFyYW1ldGVyIGVzdGltYXRlcwpuckJlc3QkcGFyCmBgYAoKIyMgYGxtKClgIGZvciBsaW5lYXIgbW9kZWxzCgotIFdlIGhhZCBjaG9zZW4gImxpbmVhciBtb2RlbCIgYXMgb3VyIG1vZGVsIGZhbWlseQotIFRoZSBgbG0oKWAgZnVuY3Rpb24gaW4gUiBpcyB2ZXJ5IGVmZmljaWVudCBmb3IgdGhlIGxpbmVhciBtb2RlbCBmYW1pbHkKICAgIC0gZmFtaWx5OiAkeSA9IGJfMCArIGJfMSAqIHhfMSArIGJfMiAqIHhfMiArIGJfMyAqIHhfMyArIC4uLiQKICAgIC0gUiBzeW50YXg6IGBsbShZIH4gWDEgKyBYMiArIFgzLCBkYXRhID0gRGF0YVNldClgCiAgICAtIGBsbSgpYCBldmVuIGZpeGVzIG91ciBwcm9ibGVtIHdpdGggYG9wdGltKClgIHRvIGd1YXJhbnRlZSBkZWxpdmVyeSBvZiBnbG9iYWwgbWluaW11bQotIFE6IHRob3VnaHRzIGFib3V0IG91ciB0aHJlZSBhcHByb2FjaGVzPwogICAgLSByYW5kb20vZ3JpZCBzZWFyY2gKICAgIC0gTmV3dG9uLVJhcGhzb24gd2l0aCBgb3B0aW0oKWAKICAgIC0gYGxtKClgCiAgICAtIChoYXZpbmcgZnVuPyB0YWtlIFNUQVQgNDQwISkKCmBgYHtyfQpzaW0xX2xtIDwtIGxtKHkgfiB4LCBkYXRhID0gc2ltMSkKc2ltMV9sbSRjb2VmZmljaWVudHMKCmBgYAoKCiMjIFF1YW50aWZ5aW5nIHVuY2VydGFpbnR5IG9mIG91ciBlc3RpbWF0ZXMKCi0gV2Ugbm93IGhhdmUgcG9pbnQgZXN0aW1hdGVzLCBidXQgdGhhdCBkb2VzIGxpdHRsZSBmb3IgdXMgdW5sZXNzIHdlIHVuZGVyc3RhbmQgdGhlICoqdW5jZXJ0YWludHkqKiBvZiB0aG9zZSBlc3RpbWF0ZXMgKGUuZy4gc3RhbmRhcmQgZXJyb3IpCiAgICAtIGBsbSgpYCBoYXMgdGhpcyBmdW5jdGlvbmFsaXR5IGJ1aWx0IGluIGZvciB1cyAKICAgIC0gYGNvbmZpbnQoKWAgaXMgYWxzbyB2ZXJ5IHVzZWZ1bCBmb3IgaW50ZXJ2YWwgZXN0aW1hdGVzIAogICAgLSBROiBob3cgY291bGQgd2UgYWNoaWV2ZSBzb21ldGhpbmcgc2ltaWxhciB1c2luZyBgb3B0aW0oKWA/CgpgYGB7cn0KIyBjb25maWRlbmNlIGludGVydmFscwpjb25maW50KGxtKHkgfiAxLCBkYXRhID0gc2ltMSkpICAjIHdoYXQgZG9lcyB0aGlzIGRvPwpjb25maW50KHNpbTFfbG0sIGxldmVsID0gMC45NSkgICAjIHdoYXQgZG9lcyB0aGlzIGRvPwoKIyBtb2RlbCBzdW1tYXJ5IHRhYmxlCm1zdW1tYXJ5KHNpbTFfbG0pCmBgYAoKIyMgUXVhbnRpZnlpbmcgdW5jZXJ0YWludHkgb2Ygb3VyIGVzdGltYXRlcwoKLSBNdWx0aXBsZSBhcHByb2FjaGVzLi4uCi0gYm9vdHN0cmFwcGluZyAKICAgIC0gc2FtcGxlIHdpdGggcmVwbGFjZW1lbnQgZnJvbSBvdXIgZGF0YQogICAgLSBjb21wdXRlIHRoZSBlc3RpbWF0ZXMgZm9yIGVhY2ggYm9vdHN0cmFwIHNhbXBsZQogICAgLSBjYWxjdWxhdGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIG91ciBib290c3RyYXAgZXN0aW1hdGVzIHRvIGFwcHJveGltYXRlIHRoZSBzdGFuZGFyZCBlcnJvcgotIGBsbSgpYCB0YWtlcyBhZHZhbnRhZ2Ugb2Ygc29tZSBuaWNlIG1hdGhlbWF0aWNhbCBhcHByb3hpbWF0aW9ucwogICAgLSBJRiB0aGUgZGF0YSBjb25mb3JtIHRvIHRoZSBhc3N1bXB0aW9ucyByZXF1aXJlZCBmb3IgdGhvc2UgbWF0aGVtYXRpY2FsIGFwcHJveGltYXRpb25zLCAKICAgICAgICAtIGBsbSgpYCBpcyBjb252ZW5pZW50LCBhY2N1cmF0ZSwgYW5kIGNvbW1vbmx5IHVzZWQKICAgICAgICAtIHRoZSBib290c3RyYXAgc2hvdWxkIGRlbGl2ZXIgc2ltaWxhciByZXN1bHRzLCB0aG91Z2gKICAgIC0gSUYgdGhlIGRhdGEgZG8gTk9UIGNvbmZvcm0gdG8gdGhlIGFzc3VtcHRpb25zLCAKICAgICAgICAtIHlvdSBtYXkgbmVlZCBzb21lIG1vcmUgYWR2YW5jZWQgc2tpbGxzIHRvIHVzZSBgbG0oKWAgcmVzcG9uc2libHkgKHRha2UgU1RBVCA0NjIpCiAgICAgICAgLSB5b3Ugc2hvdWxkIHRoaW5rIGFib3V0IGEgZGlmZmVyZW50IG1vZGVsIGZhbWlseQogICAgICAgIC0gYm9vdHN0cmFwcGluZyBtYXkgb3IgbWF5IG5vdCBiZSB1c2VmdWwgZGVwZW5kaW5nIG9uIHRoZSBpc3N1ZXMgYXQgc3Rha2UKCgpgYGB7cn0KbnJCZXN0IDwtIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgZGF0YSA9IHNpbTEpCm5yQmVzdCRwYXIKCiMgbm90ZSB0aGUgYCRwYXJgIGF0IGVuZCBvZiBgb3B0aW0oKWAuLi4gdHJ5IHdpdGhvdXQgdG8gc2VlIHdoeQpCb290c3RyYXBNb2RlbHMgPC0gCiAgbW9zYWljOjpkbygxMDAwKSAqIG9wdGltKHBhciA9IGMoMCwgMCksIGZuID0gbWVhc3VyZV9kaXN0YW5jZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzYW1wbGVfbihzaW0xLCBzaXplID0gbnJvdyhzaW0xKSwgcmVwbGFjZSA9IFRSVUUpKSRwYXIKaGVhZChCb290c3RyYXBNb2RlbHMpCgojIHN0YW5kYXJkIGVycm9yIApzZCh+IFYxLCBkYXRhID0gQm9vdHN0cmFwTW9kZWxzKSAjIGludGVyY2VwdApzZCh+IFYyLCBkYXRhID0gQm9vdHN0cmFwTW9kZWxzKSAjIHNsb3BlCgojIDk1JSBDSSAocGVyY2VudGlsZSBtZXRob2QpCnFkYXRhKH4gVjEsIHAgPSBjKDAuMDI1LCAwLjk3NSksIGRhdGEgPSBCb290c3RyYXBNb2RlbHMpICMgaW50ZXJjZXB0CnFkYXRhKH4gVjIsIHAgPSBjKDAuMDI1LCAwLjk3NSksIGRhdGEgPSBCb290c3RyYXBNb2RlbHMpICMgc2xvcGUKCmBgYAoKIyMjIyBDb21wYXJlIGBsbSgpYCByZXN1bHQgdG8gQm9vdHN0cmFwIAoKLSBOb3RlIFN0YW5kYXJkIEVycm9yIGVzdGltYXRlcyBmb3IgZWFjaCBjb2VmZmljaWVudAotIGNvbXBhcmUgY29uZmlkZW5jZSBpbnRlcnZhbCBlc3RpbWF0ZXMKLSBROiB3aGF0IGlmIHdlIHJ1biB0aGUgc2ltdWxhdGlvbiBhZ2Fpbj8KLSBROiB3aGF0IGlmIHdlIHJ1biAqKm1vcmUqKiBib290c3RyYXAgbW9kZWxzPwoKYGBge3J9CiMgcmVjYWxsIG91ciBgbG0oKWAgbW9kZWwgZm9yIGNvbXBhcmlzb24KbXN1bW1hcnkoc2ltMV9sbSkKY29uZmludChzaW0xX2xtKQoKYGBgCgoKIyMgRXhlcmNpc2U6IE1lYW4gQWJzb2x1dGUgRGV2aWF0aW9uCgotIE9uZSB3ZWFrbmVzcyB3ZSBhcmUgdG9sZXJhdGluZyBpcyBhIHNlbnNpdGl2aXR5IHRvIGV4dHJlbWUgb2JzZXJ2YXRpb25zCi0gQ29uc2lkZXIgTWVhbiBBYnNvbHV0ZSBEZXZpYXRpb24gdnMuIFJvb3QtTWVhbi1TcXVhcmUgRGV2aWF0aW9uCiAgICAtIFE6IENvbXBhcmUgJiBjb250cmFzdCB0aGUgdHdvIGFwcHJvYWNoZXMKICAgIC0gUTogSG93IGNvdWxkIHlvdSBmaXQgYSBtb2RlbCB0aGF0IG1pbmltaXplcyB0aGUgTUFEIG1ldHJpYz8KICAgICAgICAtIHVzZSBgc2ltMWAgZGF0YSBmcm9tIGBtb2RlbHJgIHBhY2thZ2U/Cgo8IS0tIERheTUgLS0+CgojIyBFdmFsdWF0aW5nIG1vZGVsIGZpdAoKLSBPdXIgbW9kZWwgZXNzZW50aWFsbHkgcGFydGl0aW9ucyB0aGUgaW5mb3JtYXRpb24gKGkuZS4gdmFyaWFiaWxpdHkpIGluIHRoZSBkYXRhIGludG8gdHdvIHBhcnRzCiAgICAxLiBzdHJ1Y3R1cmUgdGhhdCBJUyBleHBsYWluZWQgYnkgdGhlIG1vZGVsIChwcmVkaWN0aW9ucykKICAgIDIuIHJhbmRvbW5lc3MgdGhhdCBJUyBOT1QgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbCAocmVzaWR1YWxzIG9yICJlcnJvcnMiKQotIE5vdGU6IFRoZSBwb3B1bGFyIFItc3F1YXJlZCBzdGF0aXN0aWMgaXMgYSBzaW1wbGUgZGVzY3JpcHRpb24gb2YgdGhpcyBwYXJ0aXRpb24KCgojIyBNb2RlbCBwcmVkaWN0aW9ucwoKLSBSZWdyZXNzaW9uIGNvdWxkIGJlIGRlc2NyaWJlZCBhcyAiY29uZGl0aW9uYWwgZXhwZWN0YXRpb24iCi0gTGV0J3MgY29uc2lkZXIgc2tldGNoaW5nIG91dCBvdXIgZXhwZWN0YXRpb25zLCB1bmRlciB2YXJpb3VzIGNvbmRpdGlvbnMuLi4KICAgIC0gV2Ugd2FudCB0byB1c2Ugb3VyIG1vZGVsIHRvIGNhbGN1bGF0ZWQgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIFkgYXQgYSBidW5jaCBvZiBkaWZmZXJlbnQgdmFsdWVzIG9mIFgKICAgIC0gV2UgbmVlZCBhIHNlcXVlbmNlIG9mIFggdmFsdWVzIGZvciBvdXIgaW52ZXN0aWdhdGlvbiB1c2luZyBgbW9kZWxyOjpkYXRhX2dyaWQoKWAKICAgIC0gV2UnbGwgdGhlbiBjYWxjdWxhdGUgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIFkgKGkuZS4gcHJlZGljdGlvbikgYXQgZWFjaCBvbmUgdXNpbmcgYG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKClgCgoKIyMjIyBJbml0aWFsaXplIHRoZSBncmlkCgpgYGB7cn0KZ3JpZCA8LSAKICBzaW0xICU+JQogIGRhdGFfZ3JpZCh4KQoKIyBqdXN0IGEgYnVuY2ggb2YgZXZlbmx5IHNwYWNlZCBoeXBvdGhldGljYWwgdmFsdWVzIGZvciBYLi4uCmhlYWQoZ3JpZCkKCmBgYAoKIyMjIyBBZGQgc29tZSBwcmVkaWN0aW9ucyB1c2luZyBvdXIgZXhpc3RpbmcgbW9kZWwgKGBzaW0xX2xtYCkKCmBgYHtyfQpncmlkIDwtIAogIGdyaWQgJT4lCiAgYWRkX3ByZWRpY3Rpb25zKHNpbTFfbG0pCgojIHByZWRpY3Rpb25zIGFjY29tcGFueWluZyBlYWNoIGh5cG90aGV0aWNhbCBYIHZhbHVlIGluIG91ciBncmlkCmhlYWQoZ3JpZCkKYGBgCgojIyMjIFBsb3QgaXQhCgotIEFkbWl0dGVkbHksIHRoaXMgaXNuJ3QgdGhlIG1vc3QgZGlyZWN0IHdheSB0byBhZGQgYSByZWdyZXNzaW9uIGxpbmUgdG8gYSBwbG90IChlLmcuLCBgZ2VvbV9hYmxpbmUoKWApCi0gKipUaGUgYmVuZWZpdCoqOiB0aGlzIHNpbXBsZSBhcHByb2FjaCBleHRlbmRzIHRvIGp1c3QgYWJvdXQgKipBTlkqKiBtb2RlbCBpbiBSIChldmVuIGNvbXBsZXggbW9kZWxzKQotIFlvdXIgcHJpbWFyeSBsaW1pdGF0aW9uIG5vdyBpcyB2aXN1YWxpemF0aW9uIHNraWxscyAoTk9UIG1vZGVsIGNvbXBsZXhpdHkpCgpgYGB7cn0KIyBqdXN0IHRoZSBkYXRhCmdncGxvdChzaW0xLCBhZXMoeCkpICsKICBnZW9tX3BvaW50KGFlcyh5ID0geSkpCgojIGFkZCB0aGUgZ3JpZCBvZiBwcmVkaWN0aW9ucyB0byB0aGUgZGF0YQpnZ3Bsb3Qoc2ltMSwgYWVzKHgpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWQpLCBkYXRhID0gZ3JpZCwgY29sb3VyID0gInJlZCIsIHNpemUgPSAzKQoKIyBzaW5jZSB0aGUgbW9kZWwgZmFtaWx5IGlzIGxpbmVhci4uLiAKZ2dwbG90KHNpbTEsIGFlcyh4KSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSB5KSkgKwogIGdlb21fbGluZShhZXMoeSA9IHByZWQpLCBkYXRhID0gZ3JpZCwgY29sb3VyID0gInJlZCIsIHNpemUgPSAxKQpgYGAKCiMjIFJlc2lkdWFscwoKLSAqKnByZWRpY3Rpb25zKiogaWxsdXN0cmF0ZSB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEgKmNhcHR1cmVkKiBieSB0aGUgbW9kZWwKLSAqKnJlc2lkdWFscyoqIHRlbGwgeW91IHdoYXQgdGhlIG1vZGVsIGNvdWxkbid0IGNhcHR1cmUvZXhwbGFpbgoKIVtdKGFic0Vycm9yc1I0RFMucG5nKQoKCgojIyBSZXNpZHVhbHMKCi0gKipwcmVkaWN0aW9ucyoqIGlsbHVzdHJhdGUgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhICpjYXB0dXJlZCogYnkgdGhlIG1vZGVsCi0gKipyZXNpZHVhbHMqKiB0ZWxsIHlvdSB3aGF0IHRoZSBtb2RlbCBjb3VsZG4ndCBjYXB0dXJlL2V4cGxhaW4KLSBROiB3aGF0IGRvIHdlIGxlYXJuIGZyb20gdGhpcyBwbG90PwoKYGBge3J9CnNpbTEgPC0gCiAgc2ltMSAlPiUKICBhZGRfcHJlZGljdGlvbnMoc2ltMV9sbSkgJT4lCiAgYWRkX3Jlc2lkdWFscyhzaW0xX2xtKQoKIyB3ZSd2ZSBqdXN0IGFwcGVuZGVkIHRoZSBtb2RlbCByZXNpZHVhbHMgdG8gb3VyIGRhdGEgc2V0CmhlYWQoc2ltMSkKCiMgTGV0J3MgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHJlc2lkdWFscwpzaW0xICU+JQogIGdncGxvdChhZXMocmVzaWQpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC41KQoKIyBvciBjb21tb25seSB3aXRoIGEgZGVuc2l0eSBwbG90CnNpbTEgJT4lCiAgZ2dwbG90KGFlcyhyZXNpZCkpICsgCiAgZ2VvbV9kZW5zaXR5KCkKYGBgCgojIyMjIFJlc2lkdWFscyB2cyBYIChvdXIgcHJlZGljdG9yIHZhcmlhYmxlKQoKLSBROiBXaGF0IGRvIHdlIGxlYXJuIGhlcmU/CgpgYGB7cn0Kc2ltMSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHJlc2lkKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9IDIpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKCjwhLS0gUGljayB1cCBmcm9tIFI0RFMgMjMuNC4xOiBNb2RlbCBGYW1pbGllcyAgLS0+CgojIyBGb3JtdWxhIHN5bnRheCAKCi0gTW9kZWxzIHJlcXVpcmUgdGhhdCB3ZSB1c2UgYSBmb3JtdWxhIHN5bnRheCBpbiBSLCBmb3IgZXhhbXBsZSBgeSB+IHgxICsgeDJgCiAgICAtIE5vdGU6IGAgfiB5YCBpcyBzb21ldGltZXMgc2hvcnRoYW5kIGZvciBgeSB+IDFgIChlLmcuLCBgbW9zYWljYCBwYWNrYWdlKQogICAgLSBgeWAgaXMgdGhlIG91dGNvbWUgKHJlc3BvbnNlKSB2YXJpYWJsZSBvZiBpbnRlcmVzdAogICAgLSBgeDFgIGFuZCBgeDJgIGFyZSAqKmV4cGxhbmF0b3J5KiogdmFyaWFibGVzLi4uIHBvc3NpYmx5IGNvbnRpbnVvdXMgT1IgY2F0ZWdvcmljYWwKLSBGb3JtdWxhIHN5bnRheCBpcyBmYWlybHkgc2VsZi1ldmlkZW50IHdoZW4gYWxsIHZhcmlhYmxlcyBhcmUgcXVhbnRpdGF0aXZlCiAgICAtIHByZXR0eSBzaW1pbGFyIHRvIEhTIGFsZ2VicmEKICAgIC0gYHkgfiB4YCBtZWFucyAkeSA9IGEgKyBiKngkLi4uIGEgbGluZSBpbiB0d28gZGltZW5zaW9ucwogICAgLSBgeSB+IHgxICsgeDJgIG1lYW5zICR5ID0gYSArIGIqeF8xICsgYyp4XzIkLi4uIGEgbGluZSBpbiB0aHJlZSBkaW1lbnNpb25zCiAgICAtIGB5IH4geCArIEkoeF4yKWAgbWVhbnMgJHkgPSBhICsgYip4ICsgYyp4XjIkLi4uIGEgcXVhZHJhdGljCi0gVGhpbmdzIGFyZSBsZXNzIG9idmlvdXMgd2hlbiBhICoqY2F0ZWdvcmljYWwqKiB2YXJpYWJsZSBpcyBpbnZvbHZlZAogICAgLSBgeSB+IHNleGAgd2hlcmUgc2V4IGlzIGVpdGhlciAibWFsZSIgb3IgImZlbWFsZSIuLi4KICAgIC0gJHkgPSBhICsgYiooc2V4KSQgZG9lc24ndCBtYWtlIHNlbnNlLi4uIHNleCBpc24ndCBhIG51bWJlciB0aGF0IGNhbiBiZSBtdWx0aXBsaWVkCiAgICAtIFIgY29udmVydHMgYHNleGAgdG8gYW4gKippbmRpY2F0b3IqKiB2YXJpYWJsZTogYHNleG1hbGVgIChvciBgc2V4ZmVtYWxlYCkKICAgIC0gYHNleG1hbGVgIGlzIDEgaWYgYHNleGAgaXMgbWFsZSwgYW5kIDAgaWYgYHNleGAgaXMgZmVtYWxlCiAgICAtIGB5IH4gc2V4YCBiZWNvbWVzICR5ID0gYSArIGIqc2V4bWFsZSQgYW5kIHdlJ3JlIGJhY2sgaW4gYnVzaW5lc3MKLSBROiB3aHkgZG9lc24ndCBSIGNyZWF0ZSBCT1RIIGBzZXhtYWxlYCBhbmQgYHNleGZlbWFsZWA/Ci0gUTogd2h5IGFyZSB0aGVzZSBhbGwgc3RpbGwgY2FsbGVkICJsaW5lYXIiIG1vZGVscz8KCiMjIE1vZGVsaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKLSBjb25zaWRlciBhIG5ldyB0b3kgZGF0YSBzZXQgY2FsbGVkIGBzaW0yYAogICAgLSBpbnNwZWN0IHN0cnVjdHVyZSAmIHZhcmlhYmxlcwogICAgLSBwbG90IHRoZSBkYXRhCiAgICAtIGZpdCBtb2RlbCB3aXRoIGBsbSgpYAogICAgLSBnZW5lcmF0ZSBwcmVkaWN0aW9ucwotIFE6IHdoeSBhcmUgdGhlcmUgNCBwcmVkaWN0aW9ucywgYW5kIG5vdCA0MD8KCmBgYHtyfQojIGluc3BlY3QgZGF0YSBzdHJ1Y3R1cmVzCnN0cihzaW0yKQoKIyBzaG93IHNjYXR0ZXIgcGxvdCBvZiBjYXRlZ29yaWNhbCBgeGAgdmFyaWFibGUKc2ltMiAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoYWVzKHgsIHkpKQoKIyBmaXQgYSBzaW1wbGUgbW9kZWwgYW5kIGdlbmVyYXRlIHByZWRpY3Rpb25zIGFnYWluCm1vZDIgPC0gbG0oeSB+IHgsIGRhdGEgPSBzaW0yKQoKZ3JpZCA8LSAKICBzaW0yICU+JSAKICBkYXRhX2dyaWQoeCkgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhtb2QyKQpncmlkCmBgYAoKCiMjIE1vZGVsaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKLSBROiBXaHkgZG9lcyBhIG1vZGVsIHdpdGggY2F0ZWdvcmljYWwgYHhgIHdpbGwganVzdCBwcmVkaWN0IHRoZSBtZWFuIHZhbHVlIGZvciBlYWNoIGNhdGVnb3J5PwotIFE6IFdoYXQgaXMgb3VyIGRpc3RhbmNlIGNyaXRlcmlvbiBmb3IgZml0dGluZyB0aGUgImJlc3QiIG1vZGVsPwoKCmBgYHtyfQpzaW0yICU+JQogIGdncGxvdChhZXMoeCkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZ3JpZCwgYWVzKHkgPSBwcmVkKSwgY29sb3VyID0gInJlZCIsIHNpemUgPSA0KQpgYGAKCgojIyBNb2RlbGluZyB3aXRoIGludGVyYWN0aW9ucyAKCi0gc29tZXRpbWVzIHdlIG5lZWQgdG8gY29tYmluZSBpbmZvcm1hdGlvbiBhYm91dCBhIHF1YW50aXRhdGl2ZSAmIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW4gdGhlIHNhbWUgbW9kZWwgCi0gSW50ZXJwcmV0YXRpb246IHdlIGNhbid0IGV4cGxhaW4gdGhlIGltcGFjdCBvZiAqKm9uZSoqIHZhcmlhYmxlICh4MSkgd2l0aG91dCBjb25zaWRlcmluZyAqKmFub3RoZXIqKiB2YXJpYWJsZSAoeDIpCi0gdG95IGV4YW1wbGUKICAgIC0gZGF0YTogYHNpbTNgCiAgICAtIG5vdGU6IEknbSB1c2luZyBzbGlnaHRseSBpcnJlZ3VsYXIgc3ludGF4IHRvIGNsYXJpZnkgdGhlIHBvaW50CgpgYGB7cn0KIyBpbnNwZWN0IGRhdGEgc3RydWN0dXJlcwpzdHIoc2ltMykKCiMgc2hvdyBzY2F0dGVyIHBsb3Qgb2YgY2F0ZWdvcmljYWwgYHhgIHZhcmlhYmxlCnNpbTMgJT4lCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB5LCBjb2xvciA9IHgyKSkKCiMgZml0IGEgc2ltcGxlIG1vZGVsIGFuZCBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBhZ2Fpbgptb2QzXzEgPC0gbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBzaW0zKQptb2QzXzIgPC0gbG0oeSB+IHgxICsgeDIgKyB4MSp4MiwgZGF0YSA9IHNpbTMpCgojIGdlbmVyYXRlIG1vZGVsIHByZWRpY3Rpb25zCmdyaWQgPC0gCiAgc2ltMyAlPiUgCiAgZGF0YV9ncmlkKHgxLCB4MikgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhtb2QzXzEsIHZhciA9ICJtb2QxIikgJT4lCiAgYWRkX3ByZWRpY3Rpb25zKG1vZDNfMiwgdmFyID0gIm1vZDIiKQpncmlkCgpgYGAKCiMjIyMgUGxvdCB0aGUgaW50ZXJhY3Rpb24gbW9kZWwKCi0gd2Ugc2hvdWxkIHN0YWNrIChpLmUuIGdhdGhlcikgdGhlIHByZWRpY3Rpb25zIHNvIHdlIGNhbiBtYWtlIHRoZSBtb2RlbCBjb21wYXJpc29ucyBvbiB0aGUgc2FtZSBwbG90Ci0gUTogV2hhdCdzIGhhcHBlbmluZyBoZXJlPyByZWNhbGwuLi4KICAgIC0gYG1vZDNfMSA8LSBsbSh5IH4geDEgKyB4MiwgZGF0YSA9IHNpbTMpYAogICAgLSBgbW9kM18yIDwtIGxtKHkgfiB4MSArIHgyICsgeDEqeDIsIGRhdGEgPSBzaW0zKWAKICAgIC0gaW50ZXJwcmV0YXRpb24gb2YgKippbnRlcmFjdGlvbioqCgoKYGBge3J9CmdyaWRTdGFjayA8LSAKICBncmlkICU+JQogIGdhdGhlcihrZXkgPSAibW9kZWwiLCB2YWx1ZSA9ICJwcmVkIiwgbW9kMSwgbW9kMikKCiMgbW9kZWwgcGxvdApzaW0zICU+JQogIGdncGxvdChhZXMoeCA9IHgxLCB5ID0geSwgY29sb3IgPSB4MikpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBncmlkU3RhY2ssIGFlcyh5ID0gcHJlZCkpICsgCiAgZmFjZXRfd3JhcCh+IG1vZGVsKQoKIyBzYW1lIG1vZGVsLCBidXQgZmFjZXRzIHNlcGFyYXRlIGVhY2ggbW9kZWwgJiB4MiBjb21iaW5hdGlvbgpzaW0zICU+JQogIGdncGxvdChhZXMoeCA9IHgxLCB5ID0geSwgY29sb3IgPSB4MikpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBncmlkU3RhY2ssIGFlcyh5ID0gcHJlZCkpICsgCiAgZmFjZXRfZ3JpZChtb2RlbCB+IHgyKQpgYGAKCiMjIyMgV3JpdGUgZG93biBlYWNoIG1vZGVsCgotIG1hbnkgcGVvcGxlIHVuZGVyc3RhbmQgdGhlIGludGVyYWN0aW9uIGJldHRlciBieSB3cml0aW5nIGRvd24gdGhlIG1vZGVscwotIGhlcmUncyB0aGUgbW9kZWwgb3V0cHV0IHNvIHdlIGNhbiB3b3JrIHRoZW0gb3V0CgpgYGB7cn0Kc3VtbWFyeShtb2QzXzEpCnN1bW1hcnkobW9kM18yKQpgYGAKCjwhLS0gRGF5NiAtLT4KCiMjIFBsb3QgdGhlIG1vZGVsIChgbW9zYWljOjpwbG90TW9kZWwoKWApCgotIG5vdGU6IGZvciBzaW1wbGVyIG1vZGVsIGZvcm1zLCB0aGVyZSBhcmUgZ29vZCBmdW5jdGlvbnMgdG8gaGVscCAoZS5nLiwgYG1vc2FpY2AgcGFja2FnZSkKLSBzYW1lIG1vZGVscyBhcyBiZWZvcmU6CiAgICAtIGBtb2QzXzEgPC0gbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBzaW0zKWAKICAgIC0gYG1vZDNfMiA8LSBsbSh5IH4geDEgKyB4MiArIHgxKngyLCBkYXRhID0gc2ltMylgCi0gYG1wbG90KClgIGlzIGEgcHJldHR5IGhhbmR5IGZ1bmN0aW9uIAogICAgLSAqKmNvbnNvbGUqKjogYG1wbG90KHNpbTEpYCBvcGVucyBpbnRlcmFjdGl2ZSBncmFwaC1idWlsZGVyIHRvIGdldCBzdGFydGVkIHdpdGggRURBCiAgICAtICoqcmVncmVzc2lvbioqOiBgbXBsb3QobW9kZWxGaXQpYCBwcm9kdWNlcyBhIGZldyBjb21tb24gcmVzaWR1YWwgcGxvdHMgCiAgICAgICAgLSBjaG9vc2UgYSBzaW5nbGUgcGxvdCB3aXRoIGBtcGxvdChtb2RlbEZpdCwgd2hpY2ggPSAxKWAgCiAgICAgICAgLSBmb3VyLXBhY2sgb2YgY29tbW9uIHBsb3RzIHdpdGggYG1wbG90KG1vZGVsRml0KWAKICAgICAgICAtIG90aGVyIG9wdGlvbnMgYXZhaWxhYmxlCgpgYGB7cn0KIyByZXByb2R1Y2Ugb3VyIG1vZGVsIHBsb3RzCnBsb3RNb2RlbChtb2QzXzEpICAjIG5vIGludGVyYWN0aW9uIChwYXJhbGxlbCBsaW5lcykKcGxvdE1vZGVsKG1vZDNfMikgICMgaW50ZXJhY3Rpb24gICAgKG5vbi1wYXJhbGxlbC4uLnNsb3BlIGNhbiB2YXJ5IGJ5IGdyb3VwKQoKIyByZXNpZHVhbHMgdnMgZml0dGVkIHZhbHVlcwptcGxvdChtb2QzXzEsIHdoaWNoID0gMSkKbXBsb3QobW9kM18yLCB3aGljaCA9IDEpCgojIHdoYXQncyB0aGlzPwptcGxvdChtb2QzXzIsIHdoaWNoID0gNykKYGBgCgoKCiMjIEludGVyYWN0aW9ucyAoY29udGludW91czpjb250aW51b3VzKQoKLSBSZWNhbGw6IENvbnRpbnVvdXM6Q2F0ZWdvcmljYWwgaW50ZXJhY3Rpb24gYWxsb3dzIGRpZmZlcmVudCBzbG9wZSAmIGludGVyY2VwdCBkZXBlbmRpbmcgb24gdGhlIHZhbHVlIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlCi0gU2ltaWxhcmx5OiBDb250aW51b3VzOkNvbnRpbnVvdXMgaW50ZXJhY3Rpb24gYWxsb3dzIGRpZmZlcmVudCBzbG9wZSAmIGludGVyY2VwdCBkZXBlbmRpbmcgb24gdGhlIHZhbHVlIG9mIHRoZSBvdGhlciBjb250aW51b3VzIHZhcmlhYmxlLgogICAgLSBUaGluayBvZiBhIHR3aXN0ZWQgcGllY2Ugb2YgZ3JhcGggcGFwZXIuLi4gZXZlcnkgY29tYmluYXRpb24gb2YgWDE6WDIgaXMgc3RpbGwgbGluZWFyLCBidXQgdGhlIHNsb3BlcyBhbmQgaW50ZXJjZXB0cyB2YXJ5CiAgICAtIFNvbWV0aW1lcyBwbG90dGVkIGFzIDNEIHdpcmUgZGlhZ3JhbXMgKGxpa2UgdGhlIGdyYXBoIHBhcGVyIGlkZWEpCiAgICAtIE9mdGVuIHBsb3R0ZWQgYXMgMkQgaGVhdCBtYXAgb3IgY29udG91ciBwbG90CgohW2ltYWdlIGNyZWRpdDogaHR0cHM6Ly9ibG9ncy51b3JlZ29uLmVkdS9yY2x1Yi8yMDE2LzEwLzA1L2xvb2tpbmctYXQtaW50ZXJhY3Rpb25zLW9mLWNvbnRpbnVvdXMtdmFyaWFibGVzL10oaW50ZXJhY3Rpb24ucG5nKQoKCgojIyBPcmlkaW5hcnkgTGVhc3QgU3F1YXJlcyAoT0xTKSBSZWdyZXNzaW9uIFN1bW1hcnkKCi0gTGluZWFyIHJlbGF0aW9uc2hpcHMgYXJlIHZlcnkgY29udmVuaWVudCBmb3IgaW50ZXJwcmV0IGFuZCB1bmRlcnN0YW5kCi0gTXVsdGlwbGUgcmVncmVzc2lvbiBpcyBhIGdyZWF0IHRvb2wgZm9yIGF0dGFja2luZyBsb3RzIG9mIGludGVyZXN0aW5nIHF1ZXN0aW9ucwotIFRoZSBhc3N1bXB0aW9ucyBhcmUgcHJldHR5IHN0cmFpZ2h0LWZvcndhcmQgKEwtSS1OLUUpCiAgICAtICoqTCoqaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG1lYW4gcmVzcG9uc2UgKFkpIGFuZCBlYWNoIGV4cGxhbmF0b3J5IHZhcmlhYmxlIChYKQogICAgLSBvYnNlcnZhdGlvbnMgYXJlICoqSSoqbmRlcGVuZGVudAogICAgLSByZXNwb25zZXMgYXJlICoqTioqb3JtYWxseSBkaXN0cmlidXRlZCBhdCBlYWNoIGxldmVsIG9mIFgKICAgIC0gdmFyaWFuY2Ugb2YgdGhlIHJlc3BvbnNlIGlzICoqRSoqcXVhbCBmb3IgYWxsIGxldmVscyBvZiBYCgohW10oT0xTLW1vZGVsLnBuZykKCiMjIE9MUyBSZWdyZXNzaW9uIEZhaWwKCioqUHJvYmxlbTogbG90cyBvZiBpbnRlcmVzdGluZyBxdWVzdGlvbnMgY2FuJ3QgYmUgYW5zd2VyZWQgYnkgZGF0YSB0aGF0IHNhdGlzZnkgdGhvc2UgY29uZGl0aW9ucy4qKgoKQ29uc2lkZXIgYSBjb3VwbGUgb2YgZXhhbXBsZXM6IAoKLSAqKkdyYWRlcyAmIFN0dWR5aW5nKioKLSBJcyB0aGUgdGltZSBzcGVudCBzdHVkeWluZyBwcmVkaWN0aXZlIG9mIHN1Y2Nlc3Mgb24gYW4gZXhhbT8gIFRoZSB0aW1lIHNwZW50IHN0dWR5aW5nIGZvciBhbiBleGFtIChpbiBob3VycykgYW5kIHJlc3VsdCAocGFzcy9mYWlsKSBhcmUgcmVjb3JkZWQgZm9yIHJhbmRvbWx5IHNlbGVjdGVkIHN0dWRlbnRzLgoKPi0gc2luY2UgdGhlIG9iamVjdGl2ZSBpcyB1bHRpbWF0ZWx5IHRvIGFzc29jaWF0ZSB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcywgJHAkLCB3aXRoIHRoZSBjb3ZhcmlhdGVzLCBvbmUgbWlnaHQgYmUgdGVtcHRlZCB0byB0cnkgYSBsaW5lYXIgbW9kZWwgZm9yIHRoZSBhdmVyYWdlIGJpbmFyeSByZXNwb25zZSwgJHAkOiAkcF97aX0gPSBcYmV0YV97MH0gKyBcYmV0YV97MX14X3tpfSQKPi0gUTogV2hhdCdzIHRoZSAqKk9MUyB2aW9sYXRpb24qKj8gCj4tIGUuZy4sIHRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBiaW5hcnksIHdoaWNoIHZpb2xhdGVzIHRoZSBPTFMgYXNzdW1wdGlvbiBvZiBhIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHJlc3BvbnNlIGF0IGVhY2ggbGV2ZWwgb2YgWAo+LSBSZXN1bHQ6ICRwX3tpfSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfXhfe2l9JCBkb2Vzbid0IHdvcmsgd2VsbCBmb3IgYmVybm91bGxpL2Jpbm9taWFsIHJlc3BvbnNlCj4tICFbQmlub21pYWwgUmVncmVzc2lvbl0oYmlub21pYWwtbW9kZWwucG5nKQoKCiMjIE9MUyBSZWdyZXNzaW9uIEZhaWwKCioqUHJvYmxlbTogbG90cyBvZiBpbnRlcmVzdGluZyBxdWVzdGlvbnMgY2FuJ3QgYmUgYW5zd2VyZWQgYnkgZGF0YSB0aGF0IHNhdGlzZnkgdGhvc2UgY29uZGl0aW9ucy4qKgoKQ29uc2lkZXIgYSBjb3VwbGUgb2YgZXhhbXBsZXM6IAoKLSAqKkVSIFZpc2l0cyAmIFBvbGx1dGlvbioqOiAKLSBJcyB0aGUgbnVtYmVyIG9mIGFzdGhtYS1yZWxhdGVkIHZpc2l0cyB0byBhIGhvc3BpdGFsIEVtZXJnZW5jeSBSb29tIGFzc29jaWF0ZWQgd2l0aCB0aGUgYWlyIHF1YWxpdHkgaW5kZXggZm9yIHRoZSBkYXk/ICBUaGUgdG90YWwgbnVtYmVyIG9mIEVSIHBhdGllbnRzIChjb3VudCkgYW5kIHRoZSBhaXIgcXVhbGl0eSBpbmRleCAoY29udGludW91cyBtZWFzdXJlbWVudCkKCj4tIG9uZSBtaWdodCBiZSB0ZW1wdGVkIHRvIHRyeSBhbmQgbW9kZWwgJFxsYW1iZGFfe2l9JCBhcyBhIGxpbmVhciBmdW5jdGlvbiBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGU6ICRcbGFtYmRhX3tpfSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfXhfe2l9JAo+LSBROiBXaGF0J3MgdGhlICoqT0xTIHZpb2xhdGlvbioqPwo+LSBlLmcuLCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgUG9pc3NvbiBzbyB3ZSBleHBlY3QgdGhhdCBtZWFuID0gdmFyaWFuY2UgPSAkXGxhbWJkYSQsIHdoaWNoIHZpb2xhdGVzIHRoZSBPTFMgYXNzdW1wdGlvbiBvZiBlcXVhbCB2YXJpYW5jZSBmb3IgYWxsIGxldmVscyBvZiBYCj4tIFJlc3VsdDogJFxsYW1iZGFfe2l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9eF97aX0kIGRvZXNuJ3Qgd29yayB3ZWxsIGZvciBQb2lzc29uIHJlc3BvbnNlCj4tICFbUG9pc3NvbiBSZWdyZXNzaW9uXShwb2lzc29uLW1vZGVsLnBuZykKCgojIyBPTFMgUmVncmVzc2lvbiBGYWlsCgp8IFJlc2VhcmNoZXJzIHwgSW50ZXJlc3RzICB8IFZhcmlhYmxlIFR5cGUgfCAKfDotLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLXwKfCBFY29sb2dpc3RzOiB8IE51bWJlciBvZiBTcGVjaWVzIHwgUG9pc3NvbiB8CnwgQ3JpbWlub2xvZ2lzdHM6IHwgQXJyZXN0IENvdW50IHwgUG9pc3NvbiB8CnwgQ2FuY2VyIHNwZWNpYWxpc3Q6IHwgTnVtYmVyIG9mIGNhc2VzIHwgUG9pc3NvbiB8CnwgUG9saXRpY2FsIFNjaWVudGlzdHM6IHwgV2hv4oCZcyBhIGRlbW9jcmF0ZT8gfCBCZXJub3VsbGkgb3IgQmlub21pYWwgfCAKfCBQcmUtbWVkIFN0dWRlbnQ6IHwgV2hvIGdldHMgaW50byBtZWQgc2Nob29sPyAgfCBCZXJub3VsbGkgb3IgQmlub21pYWwgfAp8IFNvY2lvbG9naXN0OiB8IFdob+KAmXMgZ290IGEgdGF0dG9vPyB8IEJlcm5vdWxsaSBvciBCaW5vbWlhbCB8IAoKCiMjIEdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgKEdMTXMpCgojIyMgT25lLXBhcmFtZXRlciBleHBvbmVudGlhbCBmYW1pbHkgCgotIEluIHRoZSBlYXJseSAxOTcwcyBOZWxkZXIgJiBXZWRkZXJidXJuIGlkZW50aWZpZWQgYSBicm9hZGVyIGNsYXNzIG9mIG1vZGVscyB0aGF0IGdlbmVyYWxpemVzIHRoZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBhcHByb2FjaCB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnM6IAogICAgMS4gVGhlIHByb2JhYmlsaXR5IGZvcm11bGEgKHBkZiBvciBwbWYpIGNhbiBiZSB3cml0dGVuIGFzIGZvbGxvd3M6CiAgICAKICAgIFxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQogICAgCiAgICAyLiB0aGUgc2V0IG9mIHBvc3NpYmxlIHZhbHVlcyAoaS5lLiB0aGUgc3VwcG9ydCkgZG9lcyBub3QgZGVwZW5kIHVwb24gYSBwYXJhbWV0ZXIKCi0gSWYgc28sIGl0IGlzIHNhaWQgdG8gaGF2ZSAqKm9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZmFtaWx5IGZvcm0qKi4uLiAKCjxicj4KCiMjIyAuLi5IZXJlJ3MgdGhlIGNvb2wgcGFydCAKCj4tICRcbXUgPSAtXGZyYWN7YycoXHRoZXRhKX17YicoXHRoZXRhKX0kCgo+LSAkXHNpZ21hXjIgPSBcZnJhY3tiJycoXHRoZXRhKWMnKFx0aGV0YSkgLSBjJycoXHRoZXRhKWInKFx0aGV0YSl9e1tiJyhcdGhldGEpXV4zfSQKCj4tIChkcnVtIHJvbGwuLi4pCgo+LSAkYihcdGhldGEpID0gXGJldGFfMCArIFxiZXRhXzFYICsgLi4uJCBpcyBhICoqbGluZWFyKiogbW9kZWwhCgo+LSAkYihcdGhldGEpJCBpcyBjYWxsZWQgdGhlICoqY2Fub25pY2FsIGxpbmsqKiBmdW5jdGlvbiwgbWVhbmluZyBpdCdzIG9mdGVuIGEgZ29vZCBjaG9pY2UgdG8gbW9kZWwgYXMgYSAqKmxpbmVhcioqIGZ1bmN0aW9uIG9mIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKGJ1dCBvdGhlciBsaW5rIGZ1bmN0aW9ucyBleGlzdCBhbmQgbWlnaHQgYmUgcHJlZmVycmVkIHVuZGVyIHNwZWNpYWwgY2lyY3Vtc3RhbmNlcykuCgojIyBHTE0gZ3V0LWNoZWNrIChQb2lzc29uKQoKIyMjIEdvYWw6CgpcW2YoeTsgXHRoZXRhKT1leHBeeyhhKHkpYihcdGhldGEpICsgYyhcdGhldGEpICsgZCh5KSl9XF0KCioqV2hhdCBpZiBvdXIgcmVzcG9uc2UgdmFyaWFibGUgaXMuLi4qKgoKIyMjIFBvaXNzb24/CgotIHN1cHBvcnQ6IHNpbmNlIHBvc3NpYmxlIHZhbHVlcyBmb3IgYW55IFBvaXNzb24gcmFuZG9tIHZhcmlhYmxlIHJhbmdlIGZyb20gJDAkIHRvICRcaW5mdHkkLCB0aGUgc3VwcG9ydCBkb2Vzbid0IGRlcGVuZCBvbiAkXGxhbWJkYSQKLSAkUChZPXkpID0gXGZyYWN7ZV57LVxsYW1iZGF9XGxhbWJkYV55fXt5IX0kCi0gc2luY2UgJFxsYW1iZGFeeSA9IGVee3lsb2coXGxhbWJkYSl9JAotICRQKFk9eSkgPSBlXnstXGxhbWJkYX1lXnt5bG9nKFxsYW1iZGEpfWVeey1sb2coeSEpfSQKLSAkUChZPXkpID0gZV57eWxvZyhcbGFtYmRhKSAtIFxsYW1iZGEgLSBsb2coeSEpfSQKLSB0aGVuLCAkYihcdGhldGEpID0gbG9nKFxsYW1iZGEpJAoKIyMgR0xNIGd1dC1jaGVjayAoQmlub21pYWwpCgojIyMgR29hbDoKClxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQoKKipXaGF0IGlmIG91ciByZXNwb25zZSB2YXJpYWJsZSBpcy4uLioqCgojIyMgQmlub21pYWw/CgotIHN1cHBvcnQ6IGZvciBhbnkgdmFsdWUgb2YgJHAkLCAkMCA8IHAgPCAxJCwgYWxsIGludGVnZXIgdmFsdWVzIGZyb20gMCB0byAkbiQgYXJlIHBvc3NpYmxlIHNvIHRoZSBzdXBwb3J0IGRvZXNuJ3QgZGVwZW5kIG9uICRwJC4KLSAkUChZPXkpID0ge25cY2hvb3Nle3l9fXBeeSgxLXApXntuLXl9JAotICRQKFk9eSkgPSBlXnt5bG9nKHApICsgKG4teSlsb2coMS1wKSArIGxvZ3tuXGNob29zZXt5fX19JAotICRQKFk9eSkgPSBlXnt5bG9nKFxmcmFje3B9ezEtcH0pICsgbmxvZygxLXApICsgbG9ne25cY2hvb3Nle3l9fX0kCi0gdGhlbiwgJGIoXHRoZXRhKSA9IGxvZyhcZnJhY3twfXsxLXB9KSQKLSB0aGlzIGlzIGNhbGxlZCBhICoqbG9naXQqKiBmdW5jdGlvbiwgYW5kIGl0IGlzIGludGVycHJldGVkIGFzIHRoZSBsb2cgb2YgdGhlIG9kZHMgb2YgInN1Y2Nlc3MiIHdoZXJlIHRoZSBvYnNlcnZlZCBvdXRjb21lIGlzIGRlZW1lZCBlaXRoZXIgInN1Y2Nlc3MiIG9yICJmYWlsdXJlIiAKCgojIyBHTE0gZ3V0LWNoZWNrIChOb3JtYWwpCgojIyMgR29hbDoKClxbZih5OyBcdGhldGEpPWV4cF57KGEoeSliKFx0aGV0YSkgKyBjKFx0aGV0YSkgKyBkKHkpKX1cXQoKKipXaGF0IGlmIG91ciByZXNwb25zZSB2YXJpYWJsZSBpcy4uLioqCgojIyMgLi4uTm9ybWFsPwoKLSAkZih5KSA9IFxmcmFjezF9e1xzaWdtYVxzcXJ0KDJccGkpfWVeey1cZnJhY3soeSAtIFxtdSleMn17MlxzaWdtYV4yfX0kCgo+LSAkZih5KSA9IGVeey1sb2coXHNpZ21hKS1sb2coMlxwaSkvMn1lXnstXGZyYWN7KHkgLSBcbXUpXjJ9ezJcc2lnbWFeMn19JAo+LSBub3QgbG9va2luZyBnb29kIGZvciBvdXIgaGVybywgYnV0IGRvbid0IHBhbmljLi4uIHdoYXQgaWYgd2UgYXNzdW1lICRcc2lnbWEkIGlzIGtub3duIChpLmUuIGNvbnN0YW50KT8KPi0gJGYoeSkgXHByb3B0byBlXnstKC0yeVxtdSArIFxtdV4yICsgeV4yKX0kCj4tIHRoZW4sICRiKFx0aGV0YSkgPSBcbXUkCj4tIHNvLCAkXG11X3tZfFh9ID0gXGJldGFfezB9ICsgXGJldGFfezF9WCQKPi0gYW5kLCBvZiBjb3Vyc2UsIHBvc3NpYmxlIHZhbHVlcyBmb3IgYW55IE5vcm1hbCByYW5kb20gdmFyaWFibGUgcmFuZ2UgZnJvbSAkLVxpbmZ0eSQgdG8gJFxpbmZ0eSQsIHNvIHRoZSBzdXBwb3J0IGxvb2tzIGdvb2QhCj4tIERvZXMgdGhpcyBzcXVhcmUgd2l0aCB3aGF0IHlvdSBhbHJlYWR5IGtub3cgYWJvdXQgbXVsdGlwbGUgcmVncmVzc2lvbiB3aXRoIGEgTm9ybWFsIHJlc3BvbnNlPwoKIyMgV2hvJ3MgaW4gdGhlIGZhbWlseT8KCkhlcmUgYXJlIHNvbWUgb3RoZXIgZGlzdHJpYnV0aW9ucyB0aGF0IGNhbiBiZSB3cml0dGVuIGluIG9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZmFtaWx5IGZvcm06IAoKLSBCZXJub3VsbGkKLSBCaW5vbWlhbAotIFBvaXNzb24KLSBOb3JtYWwKLSBFeHBvbmVudGlhbAotIEdhbW1hCi0gR2VvbWV0cmljCi0gTmVnYXRpdmUgQmlub21pYWwKLSBhbmQgc29tZSBvdGhlcnMgdGhhdCBhcmVuJ3QgYXMgY29tbW9uCgoKIyMgUG9pc3NvbiBSZWdyZXNzaW9uCgotIE9uZS1wYWdlIHN1bW1hcnkgb2YgQllTSCBDaGFwdGVyIDQKLSBQb2lzc29uIFJlZ3Jlc3Npb24gQXNzdW1wdGlvbnMgKEwtTT1WLUk/IC4uLm5vdCBhcyBjYXRjaHkpOiAKICAgIC0gKipMaW5lYXJpdHkqKjogdGhlIGxvZyBvZiB0aGUgbWVhbiByYXRlICRsb2coXGxhbWJkYV97aX0pJCBtdXN0IGJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHgKICAgIC0gKipNZWFuID0gVmFyaWFuY2UqKiBieSBkZWZpbml0aW9uIGJlY2F1c2UgaXQncyBQb2lzc29uCiAgICAtICoqSW5kZXBlbmRlbmNlKioKLSBJZiB0aGUgcmVzcG9uc2UgdGhyb3dzIHlvdSBhIGN1cnZlYmFsbCBsaWtlIG92ZXJkaXNwZXJzaW9uIG9yIHRvbyBtYW55IHplcm9lcyB0aGVyZSBhcmUgc2Vuc2libGUgYWRqdXN0bWVudHMgYXZhaWxhYmxlCiAgICAtIEFjY291bnRpbmcgZm9yIG92ZXJkaXNwZXJzaW9uIChCWVNILCBwLiA4MCkKICAgIC0gWmVyby1pbmZsYXRlZCBQb2lzc29uIChaSVApIG1vZGVsIChCWVNILCBwLiA4NCkKLSBpbiBSOiAKICAgIC0gYGdsbShZIH4gWCwgZmFtaWx5ID0gcG9pc3NvbilgCiAgICAtIG5vdGU6ICoqZ2xtKiogZnVuY3Rpb24gYW5kIG5vdCB0aGUgb2xkICoqbG0qKgotIGludGVycHJldGluZyBjb2VmZmljaWVudCAkXGJldGFfezF9JCAKICAgIC0gc2luY2UgJGxvZyhcbGFtYmRhX3tYfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX1YJAogICAgLSB0aGVuLCAkZV57XGJldGFfezF9WH0gPSBcZnJhY3tcbGFtYmRhX3tYICsgMX19e1xsYW1iZGFfe1h9fSQgKHRoaXMgaXMgY2FsbGVkIGEgInJhdGUgcmF0aW8iIG9yIHNvbWV0aW1lcyAicmVsYXRpdmUgcmlzayIpCiAgICAtIGdlbmVyaWNhbGx5LCB0aGUgbWVhbiByZXNwb25zZSBjaGFuZ2VzIGJ5IGEgZmFjdG9yIG9mICRlXntcYmV0YV97MX19JCB3aXRoIGVhY2ggdW5pdCBpbmNyZWFzZSBpbiBYLiAgRm9yIGV4YW1wbGUsIGlmICRlXntcYmV0YV97MX19ID0gMC44NyQgeW91IGRlc2NyaWJlIGl0IGFzIGEgMTMlIGRlY3JlYXNlLCBhbmQgaWYgJGVee1xiZXRhX3sxfX0gPSA1LjMkIHRoZSBtZWFuIHJlc3BvbnNlIGlzIDUuMyB0aW1lcyAoY3J1ZGVseSwgNTMwJSkgaGlnaGVyLgoKIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKLSBPbmUtcGFnZSBzdW1tYXJ5IG9mIEJZU0ggQ2hhcHRlciA2Ci0gQmlub21pYWwgUmVncmVzc2lvbiBBc3N1bXB0aW9uczogCiAgICAtIEJpbm9taWFsIHJlc3BvbnNlIGlzIGEgc3VtIG9mICRuJCBCZXJub3VsbGkgdHJpYWxzCiAgICAtIFRyaWFscyBhcmUgaW5kZXBlbmRlbnQKICAgIC0gJG4kIGlzIGZpeGVkIChub3RlOiAkbiA9IDEkIGlzIGZpbmUpCiAgICAtICRwJCBpcyB0aGUgc2FtZSBmb3IgYWxsIHRyaWFscwotIE92ZXJkaXNwZXJzaW9uIGFkanVzdG1lbnQgKEJZU0gsIHAuIDExNCkKLSBpbiBSOiAKICAgIC0gYGdsbShZIH4gWCwgZmFtaWx5ID0gYmlub21pYWwpYAogICAgLSBub3RlOiAqKmdsbSoqIGZ1bmN0aW9uIGFuZCBub3QgdGhlIG9sZCAqKmxtKioKLSBpbnRlcnByZXRpbmcgY29lZmZpY2llbnQgJFxiZXRhX3sxfSQgCiAgICAtIHNpbmNlICRsb2coXGZyYWN7cH17MS1wfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX1YJAogICAgLSB0aGVuLCAkZV57XGJldGFfezF9fSA9IFxmcmFje3BfezF9LygxLXBfezF9KX17cF97MH0vKDEtcF97MH0pfSQgKHRoaXMgaXMgYW4gb2RkcyByYXRpbykKICAgIC0gYWxzbywgJHAgPSBcZnJhY3tlXntcYmV0YV97MH19ICsgZV57XGJldGFfezF9WH19ezEgKyBlXntcYmV0YV97MH19ICsgZV57XGJldGFfezF9WH19JCAgKHRoYXQncyB0aGUgcHJvYmFiaWxpdHkgb2YgInN1Y2Nlc3MiIGdpdmVuIFgpCiAgICAtIGdlbmVyaWNhbGx5LCB0aGUgb2RkcyBvZiAic3VjY2VzcyIgY2hhbmdlIGJ5IGEgZmFjdG9yIG9mICRlXntcYmV0YV97MX19JCB3aXRoIGVhY2ggdW5pdCBpbmNyZWFzZSBpbiBYLiAgRm9yIGV4YW1wbGUsIGlmICRlXntcYmV0YV97MX19ID0gMC4xMCQgdGhlIG9kZHMgaGF2ZSBkZWNyZWFzZWQgYnkgOTAlLCBhbmQgaWYgJGVee1xiZXRhX3sxfX0gPSAxMC4wJCB0aGUgb2RkcyBvZiAic3VjY2VzcyIgYXJlIDEwIHRpbWVzIChjcnVkZWx5LCAxMDAwJSkgaGlnaGVyLgoKCiMjIE9uZSBsYXN0IHRpbWUuLi4gQmFjayB0byBDaHJpcwoKIVtdKGNocmlzLnBuZykKCi0gV2hhdCBpZiB3ZSBkZWNpZGUgdG8gYXBwcm9hY2ggdGhlIHNsaWdodCBkZWxheSBkaWZmZXJlbnRseS4uLgotIE1heWJlIHdoYXQncyByZWFsbHkgaW1wb3J0YW50IHRvIENocmlzIGlzIHdoZXRoZXIgb3Igbm90IHRoZSBmbGlnaHQgKmRlcGFydHMqIG9uIHRpbWUgKHdpdGhpbiAxMCBtaW51dGVzKQogICAgLSBJZiB0aGUgcGxhbmUgKmxlYXZlcyogb24tdGltZSBhbmQgdGhlbiBhcnJpdmFsIGlzIGRlbGF5ZWQgYnkgc29tZSBwcm9ibGVtIGF0IHRoZSBkZXN0aW5hdGlvbiwgcGVyaGFwcyB0aGUgY2xpZW50IHdvdWxkIGJlIHVuZGVyc3RhbmRpbmcuCiAgICAtIFJlc3BvbnNlOiBgdGltZWx5RGVwYXJ0YCBjb2RlZCB7VFJVRTsgRkFMU0V9IGlzICoqYmluYXJ5KioKICAgIC0gRXhwbGFuYXRvcnkgVmFyaWFibGVzOiBgaG91cmAgb2YgdGhlIGRheTsgYGRvd2AgZGF5IG9mIHRoZSB3ZWVrCgoKYGBge3J9CnJlcXVpcmUobHVicmlkYXRlKQojIHNvbWUgbWlub3Igd3JhbmdsaW5nICh3aGF0IGFyZSB3ZSBkb2luZyBoZXJlPykKT3JkQ29sbGVhZ3VlRGF0YSA8LSAKICBPcmRDb2xsZWFndWVEYXRhICU+JQogIG11dGF0ZSh0aW1lbHlEZXBhcnQgPSBpZl9lbHNlKGRlcF9kZWxheSA8IDEwLCBUUlVFLCBGQUxTRSksIAogICAgICAgICBkYXRlID0geW1kKHBhc3RlMCh5ZWFyLCAiLSIsIG1vbnRoLCAiLSIsIGRheSkpLAogICAgICAgICBkb3cgPSBhcy5jaGFyYWN0ZXIod2RheShkYXRlLCBsYWJlbCA9IFRSVUUpKSkKCmBgYAoKIyMgUGxvdCB0aGUgcmVsYXRpb25zaGlwCgotIExldCdzIGxvb2sgYXQgb24tdGltZSBkZXBhcnR1cmUgc3RhdHVzIGJ5IGhvdXIgb2YgdGhlIGRheSBmb3Igb3VyIGRhdGEKCmBgYHtyfQpPcmRDb2xsZWFndWVEYXRhICU+JQogIGZpbHRlcighaXMubmEodGltZWx5RGVwYXJ0KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gaG91ciwgeSA9IGFzLm51bWVyaWModGltZWx5RGVwYXJ0KSkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMsIGhlaWdodCA9IDAuMDUpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdsbSIsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHkgPSAiYmlub21pYWwiKSwgc2UgPSAwKSArCiAgeWxhYigiT24tVGltZSBEZXBhcnR1cmUgU3RhdHVzIikKYGBgCgoKIyMgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbGluZwoKLSBBZ2Fpbiwgd2Ugc2hvdWxkIHdyaXRlIGRvd24gdGhlIG1vZGVsIHRvIGJlIHN1cmUgd2UgdW5kZXJzdGFuZCBpdC4uLgotIG5vdGUgYWJvdXQgZGlzcGVyc2lvbiBhbmQgInF1YXNpYmlub21pYWwiCgpgYGB7cn0KbG9naXRNb2QgPC0gZ2xtKHRpbWVseURlcGFydCB+IGhvdXIgKyBkb3csIGZhbWlseSA9ICJiaW5vbWlhbCIsIAogICAgICAgICAgICAgICAgZGF0YSA9IE9yZENvbGxlYWd1ZURhdGEpCm1zdW1tYXJ5KGxvZ2l0TW9kKQpjb25maW50KGxvZ2l0TW9kKQpgYGAKCgoKIyMgT3RoZXIgbW9kZWwgZmFtaWxpZXMKCi0gT25jZSB5b3XigJl2ZSBtYXN0ZXJlZCBsaW5lYXIgbW9kZWxzLCBpdCdzIGVhc3kgdG8gbGVhcm4gdGhlIG1lY2hhbmljcyBvZiBvdGhlciBtb2RlbCBjbGFzc2VzOiAKICAgIC0gKipHZW5lcmFsaXNlZCBsaW5lYXIgbW9kZWxzKiogKEdMTTsgYHN0YXRzOjpnbG0oKWApOiB1c2UgYSBzdGF0aXN0aWNhbCBpZGVhIGNhbGxlZCAibGlrZWxpaG9vZCIgdG8gYWNjb21tb2RhdGUgZWl0aGVyIGEgY29udGludW91cyAqKm9yIG5vbi1jb250aW51b3VzIHJlc3BvbnNlIHZhcmlhYmxlKiogKGUuZy4gYmluYXJ5LCBiaW5vbWlhbCwgb3IgY291bnRzKQogICAgLSAqKkdlbmVyYWxpc2VkIGFkZGl0aXZlIG1vZGVscyoqIChHQU07IGBtZ2N2OjpnYW0oKWApOiBleHRlbmQgR0xNIHRvIGluY29ycG9yYXRlIGFyYml0cmFyeSBzbW9vdGggZnVuY3Rpb25zCiAgICAtIFBlbmFsaXplZCBsaW5lYXIgbW9kZWxzIChlLmcuLCBMYXNzbzsgYGdsbW5ldDo6Z2xtbmV0KClgKTogYWRkIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBkaXN0YW5jZSBtZXRyaWMgaW4gcm9kZXIgdG8gcmVkdWNlIG1vZGVsIGNvbXBsZXhpdHkgaW4gYW4gYXR0ZW1wdCB0byBwcm9kdWNlIG1vZGVscyB0aGF0IGdlbmVyYWxpemUgYmV0dGVyIHRvIG5ldyAob3V0LW9mLXNhbXBsZSkgZGF0YXNldHMgZnJvbSB0aGUgc2FtZSBwb3B1bGF0aW9uCiAgICAtIFJvYnVzdCBsaW5lYXIgbW9kZWxzIChgTUFTUzo6cmxtKClgKTogdHdlYWsgdGhlIGRpc3RhbmNlIG1ldHJpYyB0byBiZSBsZXNzIHNlbnNpdGl2ZSB0byBvdXRsaWVycywgYXQgdGhlIGNvc3Qgb2YgYmVpbmcgbm90IHF1aXRlIGFzIGVmZmljaWVudCB3aGVuIHRoZXJlIGFyZSBubyBvdXRsaWVycy4KICAgIC0gVHJlZXMgKGUuZy4gQ0FSVDsgYHJwYXJ0OjpycGFydCgpYCk6IHVzZXMgcmVjdXJzaXZlIHBhcnRpdGlvbmluZyB0byBmaXQgYSBwaWVjZS13aXNlIGNvbnN0YW50IG1vZGVsLCBzcGxpdHRpbmcgdGhlIGRhdGEgaW50byBwcm9ncmVzc2l2ZWx5IHNtYWxsZXIgYW5kIHNtYWxsZXIgcGllY2VzLiAgCiAgICAgICAgLSBNb3N0IGVmZmVjdGl2ZSB3aGVuIHVzZWQgaW4gYWdncmVnYXRlCiAgICAgICAgLSByYW5kb20gZm9yZXN0cyAoYHJhbmRvbUZvcmVzdDo6cmFuZG9tRm9yZXN0KClgKQogICAgICAgIC0gZ3JhZGllbnQgYm9vc3RpbmcgbWFjaGluZXMgKGB4Z2Jvb3N0Ojp4Z2Jvb3N0KClgKQoKCg==